bea*_*rdc 6 haskell phantom-types
是否可以使用幻像类型编写类似身份函数的东西,以便转换类型?
例如,给定以下类型定义
data Nucleotide a = A | C | G | T | U
data RNA = RNA
data DNA = DNA
Run Code Online (Sandbox Code Playgroud)
我想写一个转换函数
r2d :: Nucleotide RNA -> Nucleotide DNA
r2d U = T
r2d x = x
Run Code Online (Sandbox Code Playgroud)
但是,这不会进行类型检查,因为单个变量x在相对侧不能具有不同的类型.
是否有可能写这个而不必枚举
r2d :: Nucleotide RNA -> Nucleotide DNA
r2d U = T
r2d A = A
r2d C = C
r2d G = G
Run Code Online (Sandbox Code Playgroud)
TL; DR:
不要创建一个可能存在无效数据的数据类型:
T :: Nucleotide RNA是可能的,这在生物学上是无意义的,因此您可能会得到r2d T(在编译时可能会阻止运行时崩溃).
请注意,Chris Drost的答案值得称赞,因为它是对所提出的技术问题的一个很好的答案.
我注意到一个潜在的崩溃来源,因为你的功能r2d并不完全 - r2d T没有定义,并且意识到这是因为你无意拥有T :: Nucleotide RNA(也没有U :: Nucleotide DNA).这是一个问题,因为任何时候你不小心(产生用户错误)r2d T你的整个程序都会崩溃.
这是您的类型中的设计缺陷.类型系统的一个重点是使无效数据无法实现,但您的代码允许T :: Nucleotide RNA甚至是T :: Nucleotide [Bool].
可悲的是,解决方案是制作更多无聊/不太光滑的类型,其中DNA的C和RNA的C之间存在区别,但是您可以使用派生Enum实例来转换它们,而无需进行所有打字.
data DNA = A | C | G | T deriving (Eq, Show, Read, Enum)
data RNA = A' | C' | G' | U' deriving (Eq, Show, Read, Enum)
r2d :: RNA -> DNA
r2d = toEnum.fromEnum
d2r :: DNA -> RNA
d2r = toEnum.fromEnum
Run Code Online (Sandbox Code Playgroud)
toEnum.fromEnum :: (Enum a, Enum b) => a -> b通过从枚举类型转换为工作Int然后从Int其他Enum类型.
现在r2d T只是一个类型错误,所以如果允许这个程序将无法编译,而使用幻像类型,如果用户设法输入无效数据,它将在运行时编译并崩溃.
(没有....)
您可能会觉得从生物/化学的角度来区分它们C并且C'它们是相同的是错误的,并且可能存在一些折衷的位置,其中您具有构造函数的幻像类型A | C | G | TU并且根据上下文不同地读取用户数据:
{-# LANGUAGE FlexibleInstances #-}
data Nucleotide a = A | C | G | TU deriving (Eq,Enum)
data RNA = RNA
data DNA = DNA
instance Show (Nucleotide DNA) where
show A = "A"
show C = "C"
show G = "G"
show TU = "T"
instance Show (Nucleotide RNA) where
show A = "A"
show C = "C"
show G = "G"
show TU = "U"
r2d :: (Nucleotide RNA) -> (Nucleotide DNA)
r2d = toEnum.fromEnum
d2r :: (Nucleotide DNA) -> (Nucleotide RNA)
d2r = toEnum.fromEnum
Run Code Online (Sandbox Code Playgroud)
有时制作一个复杂的类型只会增加你需要使用的扩展数量,如果你可以容忍几个's,你就会遇到一些潜在问题较少的东西.
在我看来,你最好使用我的第一个解决方案并编写自定义实例,Show RNA并且Read RNA用户不需要将其'放在字母的末尾.
但请注意,这read绝不是一个完整的功能(即程序崩溃的原因),并且最好不要使用readMay该safe程序包,以便您可以优雅地恢复并为您的用户提供礼貌的错误消息并有机会修复它,而不是而不是崩溃,或者通过使用Parsec或类似方法编写解析器来读取大量复杂的结构化数据,其中read或readMay不必要地慢.