gpt4 book ai didi

function - 具有不同数量参数的函数的类型类

转载 作者:行者123 更新时间:2023-12-04 14:14:33 27 4
gpt4 key购买 nike

在我简单的 Haskell DSL 中,我有以下函数来调用其他函数:

callF :: forall a. (Typeable a)
=> (V a) -> (V a)
callF fp@(V (FunP name)) =
pack $ FunAppl (prettyV fp) []

callF1 :: forall a b. (Typeable a, Typeable b)
=> (V (V a -> V b)) -> V a -> (V b)
callF1 fp@(V (FunP name)) arg =
pack $ FunAppl (prettyV fp) [prettyV arg]

callF2 :: forall a b c. (Typeable a, Typeable b, Typeable c)
=> (V (V a -> V b -> V c)) -> V a -> V b -> (V c)
callF2 fp@(V (FunP name)) arg1 arg2 =
pack $ FunAppl (prettyV fp) [prettyV arg1, prettyV arg2]

我想将这一点推广到使用类型类的任意数量的参数。

这是我尝试过的,但它仅适用于 0 或 1 个参数,并且它不强制使用正确数量的参数调用函数。
class Callable a b | a -> b where
call :: V a -> [String] -> b

instance Callable a (V b) where
call fp@(V (FunP name)) x = pack $ FunAppl (prettyV fp) x

instance (Typeable c, Typeable d) => Callable (V a -> V b) (V c -> V d) where
call fun@(V (FunP name)) list arg = call fun (list ++ [prettyV arg])

最佳答案

具有多个参数的函数的常规技术——如 printf --是使用递归类型类。对于 printf ,这是通过一个名为 PrintfType 的类来完成的。 .重要的见解是递归实例:

(PrintfArg a, PrintfType r) => PrintfType (a -> r)

这基本上是说如果你可以返回 PrintfType ,你的函数也是一个实例。 “基本情况”是一种类型,如 String .所以,如果你想调用 printf使用一个参数,它会触发两个实例: PrintfType StringPrintfType (a -> r)在哪里 rString .如果你想要两个参数,它是: String , (a -> r)在哪里 rString(a -> r)在哪里 r是以前的 (a -> r) .

但是,您的问题实际上要复杂一些。您希望有一个处理两个不同任务的实例。您希望您的实例适用于不同类型的函数(例如 V (V a -> V b)V (V a -> V b -> V c) 等),并确保提供正确数量的参数。

第一步是停止使用 [String]。传递参数。 [String] type 会丢失有关它有多少值的信息,因此您无法检查是否存在适当数量的参数。相反,您应该为参数列表使用一种类型,以反射(reflect)它有多少参数。

这种类型可能看起来像这样:
data a :. b = a :. b

它只是一种组合其他两种类型的类型,可以这样使用:
"foo" :. "bar"          :: String :. String
"foo" :. "bar" :. "baz" :: String :. String :. String

现在您只需要编写一个带有递归实例的类型类,该实例遍历类型级参数列表和函数本身。这是我的意思的一个非常粗略的独立草图;您必须自己采用它来解决您的特定问题。
infixr 8 :.
data a :. b = a :. b

class Callable f a b | f -> a b where
call :: V f -> a -> b

instance Callable rf ra (V rb) => Callable (String -> rf) (String :. ra) (V rb) where
call (V f) (a :. rest) = call (V (f a)) rest

instance Callable String () (V String) where
call (V f) () = V f

您还必须启用一些扩展: FlexibleInstances , FucntionalDepenedenciesUndecidableInstances .

然后你可以像这样使用它:
*Main> call (V "foo") ()
V "foo"
*Main> call (V (\ x -> "foo " ++ x)) ("bar" :. ())
V "foo bar"
*Main> call (V (\ x y -> "foo " ++ x ++ y)) ("bar" :. " baz" :. ())
V "foo bar baz"

如果你传入错误数量的参数,你会得到一个类型错误。诚然,这不是世界上最漂亮的错误信息!也就是说,错误的重要部分( Couldn't match type `()' with `[Char] :. ()')确实指出了核心问题(不匹配的参数列表),这应该很容易理解。
*Main> call (V (\ x -> "foo " ++ x)) ("bar" :. "baz" :. ())

<interactive>:101:1:
Couldn't match type `()' with `[Char] :. ()'
When using functional dependencies to combine
Callable String () (V String),
arising from the dependency `f -> a b'
in the instance declaration at /home/tikhon/Documents/so/call.hs:16:14
Callable [Char] ([Char] :. ()) (V [Char]),
arising from a use of `call' at <interactive>:101:1-4
In the expression:
call (V (\ x -> "foo " ++ x)) ("bar" :. "baz" :. ())
In an equation for `it':
it = call (V (\ x -> "foo " ++ x)) ("bar" :. "baz" :. ())

请注意,这对于您的特定任务可能有点过于复杂——我不相信这是解决问题的最佳方法。但这是一个很好的练习,可以使用一些更高级的类型类特性来强制执行更复杂的类型级不变量。

关于function - 具有不同数量参数的函数的类型类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16589145/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com