比方说,我在 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