Bry*_*and 17 haskell type-safety newtype type-alias
我想知道做这样的事情是否不好?
data Alignment = LeftAl | CenterAl | RightAl
type Delimiter = Char
type Width = Int
setW :: Width -> Alignment -> Delimiter -> String -> String
Run Code Online (Sandbox Code Playgroud)
而不是像这样:
setW :: Int -> Char -> Char -> String -> String
Run Code Online (Sandbox Code Playgroud)
我确实知道,有效地重构这些类型什么也没做,只是占用几行以换取更清晰的代码。但是,如果我将类型Delimiter
用于多种功能,那么对于导入此模块或稍后阅读代码的人来说,这将更加清晰。
我是Haskell的新手,所以我不知道这种类型的好方法是什么。如果这不是一个好主意,或者有一些可以提高清晰度的方法是可取的,那将是什么?
She*_*rsh 20
您使用的是类型别名,它们对代码可读性的帮助很小。但是,最好使用newtype
而不是type
更好的类型安全性。像这样:
data Alignment = LeftAl | CenterAl | RightAl
newtype Delimiter = Delimiter { unDelimiter :: Char }
newtype Width = Width { unWidth :: Int }
setW :: Width -> Alignment -> Delimiter -> String -> String
Run Code Online (Sandbox Code Playgroud)
您将处理的额外包装和展开newtype
。但是该代码在进行进一步重构时会更强大。该样式指南建议type
仅用于特殊的多态类型。
Mar*_*ann 14
我不会考虑这种不好的形式,但是显然,我并不代表整个Haskell社区。据我所知,语言功能是为特定目的而存在的:使代码更易于阅读。
可以在各种“核心”库中找到使用类型别名的示例。例如,Read
该类定义此方法:
readList :: ReadS [a]
Run Code Online (Sandbox Code Playgroud)
该ReadS
类型仅仅是一个类型别名
type ReadS a = String -> [(a, String)]
Run Code Online (Sandbox Code Playgroud)
另一个示例是中的Forest
类型Data.Tree
:
type Forest a = [Tree a]
Run Code Online (Sandbox Code Playgroud)
正如Shersh指出的,您还可以在newtype
声明中包装新类型。如果您需要以某种方式(例如,使用智能构造函数)以某种方式约束原始类型,或者想要在不创建孤立实例的Arbitrary
情况下向该类型添加功能(通常的示例是将QuickCheck 实例定义为不包含该类型的类型),则通常这很有用否则会出现这种情况)。
dan*_*iaz 10
使用newtype
-创建具有与基础类型相同的表示形式但不能替代基础类型的新类型-被认为是好的形式。这是避免原始痴迷的廉价方法,并且对于Haskell尤其有用,因为在Haskell中,函数参数的名称在签名中不可见。
新类型也可以是悬挂有用的类型类实例的地方。
鉴于新类型在Haskell中无处不在,随着时间的流逝,该语言获得了一些操纵它们的工具和习惯用法:
可强制的“魔术”类型类,当newtype构造函数在作用域内时,它可以简化新类型及其基础类型之间的转换。通常在函数实现中避免样板很有用。
ghci> coerce (Sum (5::Int)) :: Int
ghci> coerce [Sum (5::Int)] :: [Int]
ghci> coerce ((+) :: Int -> Int -> Int) :: Identity Int -> Identity Int -> Identity Int
ala
。一个惯用语(在各种程序包中实现),简化了我们可能希望与之类的函数一起使用的新类型的选择foldMap
。
ala Sum foldMap [1,2,3,4 :: Int] :: Int
GeneralizedNewtypeDeriving
。基于基础类型中可用实例的自动派生新类型实例的扩展。
DerivingVia
一个更通用的扩展,用于基于某些其他具有相同基础类型的新类型中可用的实例,为您的新类型自动派生实例。
需要注意的重要一件事是,Alignment
vs Char
不仅仅是一个清晰性问题,而是正确性之一。您的Alignment
类型表示一个事实,即只有三个有效的对齐方式,而不管有多少居民Char
。通过使用它,您可以避免使用无效的值和运算带来麻烦,并且还可以通过启用警告功能,使GHC告知您有关不完整的模式匹配的信息。
至于同义词,意见不一。就我个人而言,我认为type
小类型的同义词Int
可以通过使您针对严格相同的事物跟踪不同的名称来增加认知负荷。就是说,leftaboutabout的意义很重要,因为这种同义词在解决方案原型设计的早期阶段很有用,当您不必担心要为您的域采用的具体表示形式的细节时对象。
(值得一提的是,这里的说法type
基本上并不适用newtype
。但是用例却有所不同:用例虽然type
只是为同一事物引入了不同的名称,但实际上却newtype
引入了另一事物。这可能是一个出奇的强大举动。 -请参阅danidiaz的答案以进行进一步讨论。)