我得到了一个示意性定义的自定义数据类型:
data Foo = Foo
{ f1 :: Int
, f2 :: b
, f3 :: c
}
我需要做什么
将一个字符串附加到Foo中记录的每个函数。在我看来,由于haskell多态性,这是微不足道的。
首先尝试:类
class Value a where
val :: a -> String
class Query e where
queries :: (Value a) => [(e -> a, String)]
instance Value Int where val = show
instance Query Foo where
queries =
[ (f1, "function one")
, (other records of Foo…)
]
哪个不起作用。我不确定我弄清楚原因。但我认为ghc期望类型(Foo - > a)的函数,但得到类型的函数(Foo - > Int)。 因此,多态性不适用于此。
第二次尝试:模式匹配
keyOf :: (Foo -> a) -> String
keyOf f1 = "function one"
keyOf f2 = "function two"
keyOf f3 = "function three"
keyOf _ = "unknown function"
我很满意它会编译。然后,在ghci:
λ keyOf f2 = "function one"
无法模式匹配函数名称,显然......
编辑:为什么我需要这样做
构造一个像这样的查询字符串:
"(keyOf f1)=(f1 Foo), (keyOf f2)=(f2 Foo), (keyOf f3)=(f3 Foo)"
更一般地说,用与之关联的字符串折叠Foo中记录的每个函数,结果就是这样。例如:
exampleFoo :: Foo
exampleFoo = Foo "one" "two" "three"
assocs = [(f1, "function one"), (f2, "function two"), (f3, "function three")]
result == "function one=one, function two=two, function three=three"
现在,我真的很想知道如果没有涉及haskell中的元编程(如TemplateHaskell),这种技巧是否可行。我没有考虑的任何选项? 感谢。
答案 0 :(得分:2)
我认为您在这里寻找的是存在主义。
如果你定义
data Selector e where
Selector :: Value a => (e -> a) -> Selector e
(您需要文件顶部的{-# LANGUAGE GADTs #-}
)
然后,您可以将您的班级Query
定义为:
class Query e where
queries :: [(Selector e, String)]
instance Query Foo where
queries =
[ (Selector f1, "function one")
, (other records of Foo…)
]
您之前定义的问题是queries
的实现必须包含可以生成任何 a
类型的函数。您想要的是queries
的每个元素都会生成您选择的特定类型。这就是Selector
类型的作用 - 它会隐藏该特定类型,以便您可以选择它而不是queries
的调用者来选择。
在这个特定示例中,您可以使用a
中的Selector
类型进行唯一操作,将其转换为String
Value
,这样您就可以写得很好:
class Query e where
queries :: [(e -> String, String)]
instance Query Foo where
queries =
[ (value . f1, "function one")
, (other records of Foo…)
]
然而,如果Value
是一个更复杂的类型类,特别是一个通常有用的类型类,那么这种扁平化会变得非常冗长。
答案 1 :(得分:0)
我认为主要的问题是你对data
的工作方式感到困惑。
通过查看
keyOf :: (Foo -> a) -> String
keyOf f1 = "function one"
keyOf f2 = "function two"
keyOf f3 = "function three"
keyOf _ = "unknown function"
我想也许你的数据应该是这样的:
data Foo b c = F1 Int | F2 b | F3 c
这样,模式匹配将是:
keyOf :: Foo b c -> String
keyOf (F1 _) = "function one"
keyOf (F2 _) = "function two"
keyOf (F3 _) = "function three"
"unknown function"
不是必需的,因为我们模式匹配每个可能的Foo构造函数。