寻找类似sequenceA的函数

Looking for a function similar sequenceA

想了解有关应用程序的更多信息。

我有以下定义:

class Applicative' f where
    pack :: a -> f a
    unpack :: f a -> a

instance Applicative' Box where
    pack x = Box x
    unpack (Box x) = x

instance Applicative' Bag where
    pack x = Bag x
    unpack (Bag x) = x

instance Applicative' Basket where
    pack x = Basket x
    unpack (Basket x) = x

pack1::(Applicative' f)=>(a->b)->f a->f b
pack1 fn ta = pack (fn (unpack ta))

pack2::(Applicative' f)=>(a->b->c)->f a->f b->f c
pack2 fn ta tb = pack(fn (unpack ta) (unpack tb))

我正在寻找的是一种概括这个概念的方法(类似于 sequenceA 函数),所以应该可以通过以下方式使用这个函数(我们称之为 someFn):

somefn (\x->x+1) (Box 2)
Box 3

somefn (\x->\y->x+y) (Box 1) (Box 2)
Box 3

somefn (\x->\y->\z->x+y+z) (Box 1) (Box 2) (Box 3)
Box 6

不确定如何在 Haskell 中实现此目的。

您需要的函数类型为:

somefn :: Applicative' t => (a -> b) -> (t a -> t b)
somefn :: Applicative' t => (a -> b -> c) -> (t a -> t b -> t c)
somefn :: Applicative' t => (a -> b -> c -> d) -> (t a -> t b -> t b -> t c -> t d)

但是这些不是包含所有这些的类型,因此不存在这样的功能。特别是没有办法决定 somefn (\x y -> (x, y)) (Box 3) 应该是 Box (\y -> (3,y)) 还是 \(Box y) -> Box (3,y).

查看 liftA2liftA3 等正常 Applicatives。

您正在寻找 liftA* 函数族:

liftA :: Applicative f => (a -> b) -> f a -> fb
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA3 :: Applicative f => (a -> b -> d -> e) -> f a -> f b -> f c -> f d

在Haskell中这些函数是分开定义的。但我想您想将其概括为一个功能。这在 Haskell 中是不可能的,因为它需要更改函数的签名。 Haskell 方法是对运算符 <$><*> 进行排序,如下所示:

liftA f a = f <$> a 
liftA2 f a b = f <$> a <*> b
liftA3 f a b c = f <$> a <*> b <*> c
liftA4 f a b c d = f <$> a <*> b <*> c <*> d -- (actually not pre-defined in haskell)
.
.
.
and so on

这里发生的事情是 f <$> a 在应用容器中提升(推动)您的功能,然后您在整个 ab、[=18= 中对该功能进行排序] 和 d.

希望对您有所帮助。

正如在其他答案中已经指出的那样,您不能使用您想要的签名来编写函数。相反,您可以通过定义 (<$>)(<*>) 运算符的类似物来做类似于 Applicative 所做的事情。

您可以很容易地定义这些函数:

(<@>) :: Applicative' f => (a -> b) -> f a -> f b
f <@> x = pack . f . unpack $ x

(<%>) :: Applicative' f => f (a -> b) -> f a -> f b
f <%> x = pack . unpack f . unpack $ x

这样你就可以写

(\x y z->x+y+z) <@> Box 1 <%> Box 2 <%> Box 3 -- results in Box 6

或者,如果您不介意启用几个扩展,则可以采用不同的方法。您的 Applicative' 是比 Applicative 更强的约束,因此您可以为每个 Applicative' 实例自动派生 Functor 和 Applicative 实例,免费获得 (<$>)(<*>)

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

instance Applicative' f => Functor f where
  fmap f x = pack . f . unpack $ x

instance Applicative' f => Applicative f where
  pure = pack
  f <*> x = pack . unpack f . unpack $ x

现在我们可以写

(\x y z->x+y+z) <$> Box 1 <*> Box 2 <*> Box 3 -- results in Box 6

而且,令人高兴的是,我们还可以访问 liftA 函数族,这意味着对于相当少的参数,您实际上可以使用与您想要的函数等效的函数:

import Control.Applicative (liftA3)

liftA3 (\x y z->x+y+z) (Box 1) (Box 2) (Box 3) -- results in Box 6

可以统一这些东西。我们需要一个异构列表,表示映射到类型列表上的某种类型构造函数:

{-# LANGUAGE GADTs, DataKinds, PolyKinds, TypeOperators, PatternSynonyms #-}

import Data.Kind (Type)
import Data.Functor.Identity

infixr 4 :<, ::<
data Rec :: [k] -> (k -> Type) -> Type where
  Nil :: Rec '[] f
  (:<) :: f x -> Rec xs f -> Rec (x ': xs) f

-- For convenience and clarity:
type HList xs = Rec xs Identity

pattern HNil :: () => xs ~ '[] => HList xs
pattern HNil = Nil

pattern (::<) :: () => xxs ~ (x ': xs) => x -> HList xs -> HList xxs
pattern x ::< xs = Identity x :< xs

{-# COMPLETE HNil, (::<) #-}

现在我们可以表达我们的版本 Applicative:

class Appl f where
  appl :: (HList xs -> r) -> Rec xs f -> f r

我们可以根据Applicative定义appl:

applApplic :: Applicative f => (Rec xs Identity -> r) -> Rec xs f -> f r
applApplic f xs0 = f <$> gather xs0
  where
    gather :: Applicative f => Rec xs f -> f (Rec xs Identity)
    gather Nil = pure HNil
    gather (x :< xs) = (\y ys -> y ::< ys) <$> x <*> gather xs

我们还可以使用 appl:

定义 Applicative 方法
fmapAppl :: Appl f => (a -> b) -> f a -> f b
fmapAppl f xs = appl (\(q ::< HNil) -> f q) (xs :< Nil)

pureAppl :: Appl f => a -> f a
pureAppl x = appl (\HNil -> x) Nil

liftA2Appl :: Appl f => (a -> b -> c) -> f a -> f b -> f c
liftA2Appl f xs ys = appl (\(x ::< y ::< HNil) -> f x y) (xs :< ys :< Nil)

您可以使用 appl 作为一种 n 元提升:

appl (\(x ::< y ::< z ::< HNil) ->
       x : y ++ z) (readLn @Int :< readLn @[Int] :< readLn @[Int] :< Nil)

不幸的是,类型推断在这里不是很好。