我想在编译时创建一个映射,将编译时键映射到运行时值。如果不存在所有必需的键,函数应该能够检查这些键并抛出编译器错误。
这大致是我想要实现的目标:
class HasFirst a
first :: String
class HasMiddle a
middle :: String
class HasLast a
last :: String
print_full_name :: (HasFirst a, HasLast a) => a -> String
addFirst :: String -> a -> b
addFirst s x = -- ( Add a first name to x )
emptyName :: -- some empty name
x1 = addFirst "John" $ addLast "Smith" $ emptyName
x2 = addMiddle "Bob" $ addLast "Smith" $ emptyName
main = putStr $ print_full_name x1 -- Compiles
main = putStr $ print_full_name x2 -- No first name so compile error
对于那些具有C ++知识的人,我大致希望boost::fusion::map能做什么。
我不需要上面的代码,重要的是我可以在编译时检查参数。无论机制是课程还是其他我不介意的事情。
是否有一个包可以做到这一点或者很容易开发这个?
答案 0 :(得分:7)
让我告诉您如何使用名为phantom types的功能设计技术让您的示例工作:
{-# LANGUAGE EmptyDataDecls #-}
data YES
data NO
data Name first middle last = Name String String String
emptyName :: Name NO NO NO
emptyName = Name "" "" ""
addFirst :: String -> Name first middle last -> Name YES middle last
addFirst s (Name a b c) = Name s b c
addMiddle :: String -> Name first middle last -> Name first YES last
addMiddle s (Name a b c) = Name a s c
addLast :: String -> Name first middle last -> Name first middle YES
addLast s (Name a b c) = Name a b s
printFullName :: Name YES middle YES -> String
printFullName (Name a b c) = a++b++c
我们使用Name
的三个类型参数来跟踪已设置的名称。 printFullName
仅接受带名字和姓氏的名字。尝试打印未定义的名称时出现类型错误:
*Main> printFullName $ addFirst "John" $ addLast "Smith" $ emptyName
"JohnSmith"
*Main> printFullName $ addFirst "John" $ addMiddle "Edward" $ emptyName
<interactive>:1:16:
Couldn't match expected type `YES' against inferred type `NO'
Expected type: Name YES YES YES
Inferred type: Name YES YES NO
In the second argument of `($)', namely
`addFirst "John" $ addMiddle "Edward" $ emptyName'
In the expression:
printFullName $ addFirst "John" $ addMiddle "Edward" $ emptyName
请注意,这是一个非常粗略的编码,但希望它能够证明幻像类型的强大功能。