Haskell 将类型附加到函数
我得到了一个自定义数据类型的示意图定义如下:Haskell 将类型附加到函数,haskell,functional-programming,Haskell,Functional Programming,我得到了一个自定义数据类型的示意图定义如下: data Foo = Foo { f1 :: Int , f2 :: b , f3 :: c } "(keyOf f1)=(f1 Foo), (keyOf f2)=(f2 Foo), (keyOf f3)=(f3 Foo)" data Foo b c = F1 Int | F2 b | F3 c 我需要做什么 将字符串附加到Foo中记录的每个函数。这在我的头脑中非常清楚,由于haskell多态性,它是微不足道的 第一次尝试:课
data Foo = Foo
{ f1 :: Int
, f2 :: b
, f3 :: c
}
"(keyOf f1)=(f1 Foo), (keyOf f2)=(f2 Foo), (keyOf f3)=(f3 Foo)"
data Foo b c = 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"
显然,无法与函数名进行模式匹配
编辑:为什么我需要这样做 构造如下所示的查询字符串:
data Foo = Foo
{ f1 :: Int
, f2 :: b
, f3 :: c
}
"(keyOf f1)=(f1 Foo), (keyOf f2)=(f2 Foo), (keyOf f3)=(f3 Foo)"
data Foo b c = F1 Int | F2 b | F3 c
更一般地说,折叠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),这种技巧是否可行。我没有考虑过什么选择吗?
谢谢。我认为主要的问题是您混淆了
数据的工作原理
看
keyOf :: (Foo -> a) -> String
keyOf f1 = "function one"
keyOf f2 = "function two"
keyOf f3 = "function three"
keyOf _ = "unknown function"
我认为你的数据应该是这样的:
data Foo = Foo
{ f1 :: Int
, f2 :: b
, f3 :: c
}
"(keyOf f1)=(f1 Foo), (keyOf f2)=(f2 Foo), (keyOf f3)=(f3 Foo)"
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"
不需要“未知函数”
,因为我们正在对每个可能的Foo构造函数进行模式匹配。我认为您在这里寻找的是一个存在的
如果你定义
data Selector e where
Selector :: Value a => (e -> a) -> Selector e
(文件顶部需要{-#LANGUAGE GADTs}
)
然后,您可以将类查询定义为:
class Query e where
queries :: [(Selector e, String)]
instance Query Foo where
queries =
[ (Selector f1, "function one")
, (other records of Foo…)
]
您以前定义的问题是查询的实现必须包含可以生成任何类型的函数。您需要的是查询的每个元素生成您选择的特定类型。这就是选择器
类型所做的-它隐藏特定类型,以便您可以选择它,而不是查询的调用者
可以选择
在这个特定的示例中,使用选择器中的a
类型所能做的唯一一件事就是将其转换为具有值的字符串,这样您就可以编写:
class Query e where
queries :: [(e -> String, String)]
instance Query Foo where
queries =
[ (value . f1, "function one")
, (other records of Foo…)
]
然而,如果Value
是一个更复杂的类型类,尤其是一个通常有用的类型类,那么这种扁平化会变得非常冗长。你能给出一个你想用每个函数所附字符串编写的程序的例子吗?是的,我编辑了这个问题。这足够清楚吗?恐怕不清楚-我不明白你关于查询字符串的意思。使用一些示例代码或伪代码可能会更清晰。我知道这有点困惑:我知道我的想法是错误的,但我想不出其他设计。我想我现在明白了-看我的答案。Foo数据类型由13条记录组成,我需要它们中的每一条来构造我的最终查询字符串,所以我认为sum类型与此无关。在这种情况下,模式匹配可能不是您需要的东西。这可能是问题的正确答案,但请不要在没有说明它们的情况下推荐存在主义(我实际上倾向于在好的用例中使用存在主义,但我相当怀疑这是一个)。这是一个好主意,但[(选择器e,字符串)]
将生成实例查询Foo中的[(选择器Foo,String)]
列表。然后,Selector f1::Selector Int
将不适用于该上下文。有一个输入错误,它应该是Selector::Value a=>(e->a)->Selector e
。但在这里你可能不需要存在主义。对于类型选择器e
的值,您可以做的唯一一件事是将包含的函数应用于类型e
的值,但是由于类型a
是存在量化的,因此您可以对结果做的唯一一件事就是对其调用val
,因为这是你所知道的关于a
:它是值的一个实例。由于val
只是生成一个字符串,所以您最好只使用数据选择器e=Selector(e->string)string
和查询::[Selector]
。我同意这在这个特定的上下文中并不严格合适,但堆栈溢出会鼓励将问题简化为基本问题。对于更复杂的类型类,但基本问题相同,这将是完全合理的。