我在Javascript中有这个polyvariadic comp函数,并且想知道Haskell中的类似实现是否可行.我最感兴趣的是comp的类型:
const comp = f => Object.assign( g => comp([g].concat(f)),{run: x => f.reduce((acc,h) => h(acc),x)} ); const inc = n => n + 1; const sqr = n => n * n; const repeatStr = s => n => Array(n + 1).join(s); comp(repeatStr("*")) (inc) (sqr).run(2); // "*****" comp(repeatStr("*")) (inc) (inc) (inc) (inc) (inc).run(0); // "*****"
comp构建一个异构数组,通常在Haskell中没有类型.我猜这种可变参数函数在返回类型中必须是多态的.但是,到目前为止,这项任务超出了我的Haskell知识.任何线索都会有所帮助.
上下文
我使用Javascript运行时类型检查器,以便我可以以类型安全的方式在comp中构建数组.它需要显式类型注释,并且仅支持参数和秩-2多态.
解决方法
{-# LANGUAGE GADTs #-} data Comp a b where Id :: Comp a a Comp :: Comp b c -> (a -> b) -> Comp a c run :: Comp a b -> a -> b run Id = id run (Comp g f) = run g . f
Id构造函数类似于[],Comp构造函数类似于:但是翻转了参数.
接下来,我们使用varargs pattern创建一个多变量函数.为此,我们定义了一个类型类:
{-# LANGUAGE MultiParamTypeClasses,FunctionalDependencies #-} class Chain a b c | c -> a where chain :: Comp a b -> c
请注意,我们的状态是Comp b c,我们的结果是Comp b c或带有另一个函数(a – > b)作为输入的函数,并将其与我们的状态组合以生成一个名为r的新链,状态为Comp a c.我们现在为这些定义实例:
{-# LANGUAGE FlexibleInstances #-} instance c ~ c' => Chain b c (Comp b c') where chain = id instance Chain a c r => Chain b c ((a -> b) -> r) where chain g f = chain (Comp g f) comp :: Chain b b c => c comp = chain Id
现在可以将comp函数定义为链ID(即,以空列表Id作为其状态的链).我们最终可以像在JavaScript中一样使用这个comp函数:
inc :: Int -> Int inc = (+1) sqr :: Int -> Int sqr x = x * x repeatStr :: String -> Int -> String repeatStr s x = concat (replicate x s) example1 :: String example1 = comp (repeatStr "*") inc sqr `run` 2 example2 :: String example2 = comp (repeatStr "*") inc inc inc inc inc `run` 0
把它们放在一起:
{-# LANGUAGE GADTs,MultiParamTypeClasses,FunctionalDependencies,FlexibleInstances #-} data Comp a b where Id :: Comp a a Comp :: Comp b c -> (a -> b) -> Comp a c run :: Comp a b -> a -> b run Id = id run (Comp g f) = run g . f class Chain a b c | c -> a where chain :: Comp a b -> c instance c ~ c' => Chain b c (Comp b c') where chain = id instance Chain a c r => Chain b c ((a -> b) -> r) where chain g f = chain (Comp g f) comp :: Chain b b c => c comp = chain Id inc :: Int -> Int inc = (+1) sqr :: Int -> Int sqr x = x * x repeatStr :: String -> Int -> String repeatStr s x = concat (replicate x s) example1 :: String example1 = comp (repeatStr "*") inc sqr `run` 2 example2 :: String example2 = comp (repeatStr "*") inc inc inc inc inc `run` 0
如您所见,comp的类型是Chain b b c => C.要定义Chain类型,我们需要MultiParamTypeClasses和FunctionalDependencies.要使用它,我们需要FlexibleInstances.因此,您需要一个复杂的JavaScript运行时类型检查器才能正确键入check comp.
编辑:在注释中指出naomik和Daniel Wagner,您可以使用实际函数而不是可组合函数列表作为comp状态的内部表示.例如,在JavaScript中:
const comp = run => Object.assign(g => comp(x => g(run(x))),{run});
同样,在Haskell中:
{-# LANGUAGE GADTs,FlexibleInstances #-} newtype Comp a b = Comp { run :: a -> b } class Chain a b c | c -> a where chain :: Comp a b -> c instance c ~ c' => Chain b c (Comp b c') where chain = id instance Chain a c r => Chain b c ((a -> b) -> r) where chain g f = chain (Comp (run g . f)) comp :: Chain b b c => c comp = chain (Comp id)
请注意,即使我们不再使用GADT,我们仍然需要GADTs语言扩展,以便在Chain的第一个实例中使用等式约束c~c’.另外,你可以看到运行g. f已从run的定义转移到Chain的第二个实例.同样,id已从run的定义移到comp的定义中.
(1)您可以使用存在类型在Haskell中创建异构函数列表,但它们不具有可组合的附加约束.