假设我在 Haskell 中有一个约定,我定义了一系列如下函数:
data Node = MkNode
s0 :: Node -> s -> Node
s0 a _ = a
s1 :: (s -> a) -> (a -> Node) -> s -> Node
s1 a b c = b (a c)
s2 :: (s -> a) -> (s -> b) -> (a -> b -> Node) -> s -> Node
s2 a b c d = c (a d) (b d)
s3 :: (s -> a) -> (s -> b) -> (s -> c) -> (a -> b -> c -> Node) -> s -> Node
s3 a b c d e = d (a e) (b e) (c e)
如果可能的话,我希望定义一个函数 sn
,它接受可变数量的参数,并且始终采用这种模式。我在使用类型类之前见过这种事情,但我不太清楚在这种情况下如何做。例如,我可以想象:
class NAble elt where
sn :: elt -> state -> Node
instance NAble Node where
sn elt _ = elt
但后来我陷入困境。我不确定递归定义是什么。也许是这样的:
instance (NAble b) => NAble (a -> b) where
sn eltMaker state = ss (eltMaker state) state
但这显然不太正确。不确定这是否可能,但如果可以的话那就太酷了。当然,如果这有助于正确的话,参数的顺序可以改变,但是让它发挥作用真是太好了。任何帮助将不胜感激!
最佳答案
如果您以稍微不同的顺序放置参数 - 使用 s
参数首先出现,而 Node
- 其次构造函数——它变得容易多了。然后类型系列将立即修复您:
{-# LANGUAGE TypeFamilies #-}
data Node = MkNode
class NAble t where
type Ret t s
sn :: s -> t -> Ret t s
instance NAble Node where
type Ret Node s = Node
sn s mkNode = mkNode
instance NAble t => NAble (a -> t) where
type Ret (a -> t) s = (s -> a) -> Ret t s
sn s mkNode fa = sn s (mkNode (fa s))
但让我也推荐一个替代方案。查看标准库使用的模式:
pure :: Applicative f => ( t) -> f t
fmap :: Applicative f => (a -> t) -> f a -> f t
liftA2 :: Applicative f => (a -> b -> t) -> f a -> f b -> f t
liftA3 :: Applicative f => (a -> b -> c -> t) -> f a -> f b -> f c -> f t
服用f~(->) s
和t~Node
,我们得到:
pure :: ( Node) -> s -> Node
fmap :: (a -> Node) -> (s -> a) -> s -> Node
liftA2 :: (a -> b -> Node) -> (s -> a) -> (s -> b) -> s -> Node
liftA3 :: (a -> b -> c -> Node) -> (s -> a) -> (s -> b) -> (s -> c) -> s -> Node
如果使用标准库的人需要liftA4
怎么办?或更高?通常他们会切换到 (<*>)
链使用:
(<*>) :: (s -> a -> Node) -> (s -> a) -> s -> Node
(f <*> g) s = f s (g s)
{-# MAKE_THE_PROGRAMMER_INLINE liftAn #-}
liftAn mkNode f1 f2 ... fn = pure mkNode
<*> f1
<*> f2
...
<*> fn
关于haskell - 在 Haskell 中使用类型类作为可变参数模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61440490/