我完全是Haskell的初学者。在试图解决hackerrank上的一些练习练习时,我偶然发现了一个错误,这让我怀疑"正确的方法"(tm)。
我想做的是:
import Data.Matrix
newtype Puzzle = Matrix Char
complete :: Puzzle -> Bool
complete p = '-' `elem` (toList p)
[... more functions on 'Matrix Char']
,这给了我:
Couldn't match expected type ‘Matrix Char’
with actual type ‘Puzzle’
In the first argument of ‘toList’, namely ‘p’
In the second argument of ‘elem’, namely ‘(toList p)’
显而易见的解决方案当然是使用Matrix Char
而不是Puzzle
。但我不觉得这是一个优雅的解决方案。抽象为更具体的类型感觉是正确的方法…
使用type
而不是newtype
。前者创建一个类型别名,后者是一个新的类型声明。特别是newtype
是data
的一种特殊情况,其中新类型表示对现有类型的"包装"(这是一种可以由编译器优化的情况)。
我认为比Jeffrey的回答更好的解决方案,至少在比玩具游戏更实质性的代码库的情况下,是继续使用newtype
,但将代码更改为:
import Data.Matrix
newtype Puzzle = Puzzle (Matrix Char)
complete :: Puzzle -> Bool
complete (Puzzle matrix) = '-' `elem` toList matrix
这将允许您继续使用真正不同的数据类型,而不是诉诸类型别名,后者不会引入任何新类型,并且允许Puzzle
和Matrix Char
完全可互换使用,而不增加类型安全性(也没有表达性)。
此外,Jeffrey是对的,newtype
比type
更类似于data
——newtype
比data
提供了一些性能优化,但更受限制,稍微影响程序评估语义。您最好仔细阅读Haskell中定义类型和类型别名的所有方法。
在你的情况下,你可以用data
代替newtype
而不改变程序的行为;程序的其余部分应该继续相同地工作。
参见:关于类型安全的Haskell类型与newtype