在Haskell中使用一个类型类来实现变量参数模式。

问题描述 投票:1回答:1

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

但这显然是不对的 不知道这是否可能,但如果可能的话,那就很酷了。当然,如果这有助于获得正确的结果,可以改变参数的顺序,但如果能让它工作起来就更好了。任何帮助都将被感激!

haskell recursion typeclass
1个回答
2
投票

如果你把参数的顺序稍微不同--用 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~(->) st~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
© www.soinside.com 2019 - 2024. All rights reserved.