制作 Applicative 的实例
Making instance of Applicative
仍然不能百分百确定如何创建更复杂类型的实例。有这个:
data CouldBe a = Is a | Lost deriving (Show, Ord)
创建了一个Functor
的实例,以Maybe
为例:
instance Functor CouldBe where
fmap f (Is x) = Is (f x)
fmap f Lost = Lost
做这样的事情:
tupleCouldBe :: CouldBe a -> CouldBe b -> CouldBe (a,b)
tupleCouldBe x y = (,) <$> x <*> y
CouldBe
需要成为 Applicative
的一个实例,但是您会怎么做呢?当然我可以查找并复制它,但我想了解它背后的过程并最终以 instance
CouldBe 的声明结束。
你直接写出来,类型如下:
instance Applicative CouldBe where
{-
<b>Minimal complete definition:</b>
pure, ((<*>) | liftA2)
pure :: a -> f a
pure :: a -> CouldBe a
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 :: (a -> b -> c) -> CouldBe a -> CouldBe b -> CouldBe c
-}
pure a = fa
where
fa = ....
liftA2 abc fa fb = fc
where
fc = ....
根据
data CouldBe a = Is a | Lost
我们的工具集是
Is :: a -> CouldBe a
Lost :: CouldBe a
但我们也可以使用模式匹配,例如
couldBe is lost (Is a) = is a
couldBe is lost (Lost) = lost
couldBe :: ? -> ? -> CouldBe a -> b
couldBe :: ? -> b -> CouldBe a -> b
couldBe :: (a -> b) -> b -> CouldBe a -> b
所以,
-- pure :: a -> f a
pure :: a -> CouldBe a
匹配
Is :: a -> CouldBe a
所以我们定义
pure a = Is a
然后,对于liftA2
,我们遵循数据案例:
-- liftA2 :: (a -> b -> c) -> f a -> f b -> f c
-- liftA2 :: (a -> b -> c) -> CouldBe a -> CouldBe b -> CouldBe c
liftA2 abc Lost _ = ...
liftA2 abc _ Lost = ...
liftA2 abc (Is a) (Is b) = fc
where
c = abc a b
fc = .... -- create an `f c` from `c`:
-- do we have a `c -> CouldBe c` ?
-- do we have an `a -> CouldBe a` ? (it's the same type)
但在前两种情况下,我们没有 a
或 b
;所以我们必须凭空想出一个CouldBe c
。我们的 我们的工具集 中也有此工具。
完成所有缺失的部分后,我们可以将表达式直接代入定义,消除所有不需要的中间值/变量。
使用我的新 idiomatic 包,您可以导出 Applicative
求和。
{-# Language DataKinds #-}
{-# Language DeriveGeneric #-}
{-# Language DerivingVia #-}
{-# Language StandaloneKindSignatures #-}
import Data.Kind
import GHC.Generics
import Generic.Applicative
type CouldBe :: Type -> Type
data CouldBe a = Is a | Lost
deriving
stock (Eq, Ord, Show, Generic1)
-- > pure @CouldBe 10
-- Is 10
-- > liftA2 (+) (Is 1) Lost
-- Lost
-- > liftA2 (+) Lost (Is 10)
-- Lost
deriving (Functor, Applicative)
via Idiomatically CouldBe '[LeftBias Terminal]
-- > tupleCouldBe Lost Lost
-- Lost
-- > tupleCouldBe (Is 1) Lost
-- Lost
-- > tupleCouldBe Lost (Is 20)
-- Lost
-- > tupleCouldBe (Is 1) (Is 20)
-- Is (1,20)
tupleCouldBe :: CouldBe a -> CouldBe b -> CouldBe (a, b)
tupleCouldBe = liftA2 (,)
为什么这行得通? left-biased 意味着我们选择 Is
构造函数作为 pure
构造函数。
这意味着当提升不同的构造函数时,我们从那个构造函数“背离”。
Terminal
描述了我们如何将任何 Applicative
转换为 Const mempty
data Terminal
instance (Applicative f, Monoid m) => Idiom Terminal f (Const m) where
idiom :: f ~> Const m
idiom = mempty
在这种情况下丢弃 Is
映射到 Lost
的参数。
请注意,无法定义 CouldBe
的 right-biased 定义,因为它需要一个从无到有产生 a
的应用态射
via Idiomatically CouldBe '[RightBias ..]
idiom :: Const () ~> Identity
idiom (Const ()) = Identity ??
仍然不能百分百确定如何创建更复杂类型的实例。有这个:
data CouldBe a = Is a | Lost deriving (Show, Ord)
创建了一个Functor
的实例,以Maybe
为例:
instance Functor CouldBe where
fmap f (Is x) = Is (f x)
fmap f Lost = Lost
做这样的事情:
tupleCouldBe :: CouldBe a -> CouldBe b -> CouldBe (a,b)
tupleCouldBe x y = (,) <$> x <*> y
CouldBe
需要成为 Applicative
的一个实例,但是您会怎么做呢?当然我可以查找并复制它,但我想了解它背后的过程并最终以 instance
CouldBe 的声明结束。
你直接写出来,类型如下:
instance Applicative CouldBe where
{-
<b>Minimal complete definition:</b>
pure, ((<*>) | liftA2)
pure :: a -> f a
pure :: a -> CouldBe a
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 :: (a -> b -> c) -> CouldBe a -> CouldBe b -> CouldBe c
-}
pure a = fa
where
fa = ....
liftA2 abc fa fb = fc
where
fc = ....
根据
data CouldBe a = Is a | Lost
我们的工具集是
Is :: a -> CouldBe a
Lost :: CouldBe a
但我们也可以使用模式匹配,例如
couldBe is lost (Is a) = is a
couldBe is lost (Lost) = lost
couldBe :: ? -> ? -> CouldBe a -> b
couldBe :: ? -> b -> CouldBe a -> b
couldBe :: (a -> b) -> b -> CouldBe a -> b
所以,
-- pure :: a -> f a
pure :: a -> CouldBe a
匹配
Is :: a -> CouldBe a
所以我们定义
pure a = Is a
然后,对于liftA2
,我们遵循数据案例:
-- liftA2 :: (a -> b -> c) -> f a -> f b -> f c
-- liftA2 :: (a -> b -> c) -> CouldBe a -> CouldBe b -> CouldBe c
liftA2 abc Lost _ = ...
liftA2 abc _ Lost = ...
liftA2 abc (Is a) (Is b) = fc
where
c = abc a b
fc = .... -- create an `f c` from `c`:
-- do we have a `c -> CouldBe c` ?
-- do we have an `a -> CouldBe a` ? (it's the same type)
但在前两种情况下,我们没有 a
或 b
;所以我们必须凭空想出一个CouldBe c
。我们的 我们的工具集 中也有此工具。
完成所有缺失的部分后,我们可以将表达式直接代入定义,消除所有不需要的中间值/变量。
使用我的新 idiomatic 包,您可以导出 Applicative
求和。
{-# Language DataKinds #-}
{-# Language DeriveGeneric #-}
{-# Language DerivingVia #-}
{-# Language StandaloneKindSignatures #-}
import Data.Kind
import GHC.Generics
import Generic.Applicative
type CouldBe :: Type -> Type
data CouldBe a = Is a | Lost
deriving
stock (Eq, Ord, Show, Generic1)
-- > pure @CouldBe 10
-- Is 10
-- > liftA2 (+) (Is 1) Lost
-- Lost
-- > liftA2 (+) Lost (Is 10)
-- Lost
deriving (Functor, Applicative)
via Idiomatically CouldBe '[LeftBias Terminal]
-- > tupleCouldBe Lost Lost
-- Lost
-- > tupleCouldBe (Is 1) Lost
-- Lost
-- > tupleCouldBe Lost (Is 20)
-- Lost
-- > tupleCouldBe (Is 1) (Is 20)
-- Is (1,20)
tupleCouldBe :: CouldBe a -> CouldBe b -> CouldBe (a, b)
tupleCouldBe = liftA2 (,)
为什么这行得通? left-biased 意味着我们选择 Is
构造函数作为 pure
构造函数。
这意味着当提升不同的构造函数时,我们从那个构造函数“背离”。
Terminal
描述了我们如何将任何 Applicative
转换为 Const mempty
data Terminal
instance (Applicative f, Monoid m) => Idiom Terminal f (Const m) where
idiom :: f ~> Const m
idiom = mempty
在这种情况下丢弃 Is
映射到 Lost
的参数。
请注意,无法定义 CouldBe
的 right-biased 定义,因为它需要一个从无到有产生 a
的应用态射
via Idiomatically CouldBe '[RightBias ..]
idiom :: Const () ~> Identity
idiom (Const ()) = Identity ??