fre*_*red 2 primitive haskell types
来自http://learnyouahaskell.com/making-our-own-types-and-typeclasses
data Person = Person { name :: String
, age :: Int
} deriving (Show)
Run Code Online (Sandbox Code Playgroud)
在一个真实的应用程序中,使用像String和Int这样的原语作为名称和年龄将构成原始的痴迷,代码气味.(显然,出生日期优于国际时代,但让我们忽略它)相反,人们更喜欢像
newtype Person = Person { name :: Name
, age :: Age
} deriving (Show)
Run Code Online (Sandbox Code Playgroud)
在OO语言中,这看起来像
class Person {
Name name;
Age age;
Person(Name name, Age age){
if (name == null || age == null)
throw IllegalArgumentException();
this.name = name;
this.age = age;
}
}
class Name extends String {
Name(String name){
if (name == null || name.isEmpty() || name.length() > 100)
throw IllegalArgumentException();
super(name);
}
}
class Age extends Integer {
Age(Integer age){
if (age == null || age < 0)
throw IllegalArgumentException();
super(age);
}
}
Run Code Online (Sandbox Code Playgroud)
但在惯用的最佳实践Haskell中如何实现同样的目标?
制作Name抽象并提供智能构造函数.这意味着您不导出Name数据构造函数,Maybe而是提供 - 返回构造函数:
module Data.Name
( Name -- note: only export type, not data constructor
, fromString
, toString
) where
newtype Name = Name String
fromString :: String -> Maybe Name
fromString n | null n = Nothing
| length n > 100 = Nothing
| otherwise = Just (Name n)
toString :: Name -> String
toString (Name n) = n
Run Code Online (Sandbox Code Playgroud)
现在不可能Name在该模块之外构造错误长度的值.
因为Age,你可以做同样的事情,或使用类型Data.Word,或使用以下低效但有保证的非负表示:
data Age = Zero | PlusOne Age
Run Code Online (Sandbox Code Playgroud)