Known/estabilished 同源对的 monad 实例的用例
Known/estabilished usecases for the monad instance of an homogeneous pair
有一次我问this question, which was correctly marked as duplicate of 。
现在我很好奇,一对同类类型的 monad 实例是否有任何已知的用例?
这是它的实例:
data Pair a = Pair a a deriving Show
instance Functor Pair where
fmap f (Pair a b) = Pair (f a) (f b)
instance Applicative Pair where
pure a = Pair a a
Pair f g <*> Pair x y = Pair (f x) (g y)
instance Monad Pair where
m >>= f = joinPair (f <$> m)
joinPair :: Pair (Pair a) -> Pair a
joinPair (Pair (Pair x _) (Pair _ y)) = Pair x y
我以前从来没有用过这样的monad,但是在想出这个例子之后,我可以看到它的优点。这将计算两个笛卡尔坐标之间的距离。它似乎实际上非常有用,因为它会自动将 Xs 上的任何操作与 Ys 上的任何操作分开。
collapsePair :: (a -> a -> b) -> Pair a -> b
collapsePair f (Pair x y) = f x y
type Coordinates = Pair Float
type Distance = Float
type TriangleSides = Pair Distance
-- Calculate the sides of a triangle given two x/y coordinates
triangleSides :: Coordinates -> Coordinates -> TriangleSides
triangleSides start end = do
-- Pair x1 y1
s <- start
-- Pair x2 y2
e <- end
-- Pair (x2 - x1) (y2 - y1)
Pair (e - s) (e - s)
-- Calculate the cartesian distance
distance :: Coordinates -> Coordinates -> Distance
distance start end = collapsePair distanceFormula (triangleSides start end)
where distanceFormula x y = sqrt (x ^ 2 + y ^ 2)
编辑:
原来这个例子可以只用 Pair
的 Applicative
实例来完成;不需要 Monad
:
import Control.Applicative
triangleSides :: Coordinates -> Coordinates -> TriangleSides
triangleSides = liftA2 (flip (-))
但是,我们可以通过在末尾的 X 上加 1,以人为的方式让它依赖于 Monad
:
triangleSides' :: Coordinates -> Coordinates -> TriangleSides
triangleSides' start end = do
s <- start
e <- end
Pair (e - s + 1) (e - s)
在这种情况下,最后一行 不能 转换为某种形式的 pure ...
因此必须使用 Monad 实例。
可以进一步探索的有趣之处在于,我们可以轻松地将其扩展为包括三维坐标(或更多)。我们可以使用 Representable
的默认实例,并通过 Data.Functor.Rep.
的 Co
新类型派生 Applicative
和 Monad
实例
然后我们可以将distance
计算抽象成它自己的类型类,它会在n维坐标上工作,只要我们为坐标类型写一个Distributive
实例。
{-# Language DeriveAnyClass #-}
{-# Language DeriveGeneric #-}
{-# Language DeriveTraversable #-}
{-# Language DerivingVia #-}
import Control.Applicative
import Data.Distributive
import Data.Functor.Rep
import GHC.Generics
class (Applicative c, Foldable c) => Coordinates c where
distance :: Floating a => c a -> c a -> a
distance start end = sqrt $ sum $ fmap (^2) $ liftA2 (-) end start
data Triple a = Triple
{ triple1 :: a
, triple2 :: a
, triple3 :: a
}
deriving ( Show, Eq, Ord, Functor, Foldable, Generic1, Representable
, Coordinates )
deriving (Applicative, Monad) via Co Triple
instance Distributive Triple where
distribute f = Triple (triple1 <$> f) (triple2 <$> f) (triple3 <$> f)
> distance (Triple 7 4 3) (Triple 17 6 2)
10.246950765959598
你可以验证这个答案here。
你的 Pair a
同构于 Reader Bool a
/Bool -> a
:
to (Pair f t) = \b -> if b then t else f
from f = Pair (f False) (f True)
因此,the Reader monad 的任何用例也是您的 monad 的潜在用例。这些数据类型的通用术语是 可表示仿函数。
有一次我问this question, which was correctly marked as duplicate of
现在我很好奇,一对同类类型的 monad 实例是否有任何已知的用例?
这是它的实例:
data Pair a = Pair a a deriving Show
instance Functor Pair where
fmap f (Pair a b) = Pair (f a) (f b)
instance Applicative Pair where
pure a = Pair a a
Pair f g <*> Pair x y = Pair (f x) (g y)
instance Monad Pair where
m >>= f = joinPair (f <$> m)
joinPair :: Pair (Pair a) -> Pair a
joinPair (Pair (Pair x _) (Pair _ y)) = Pair x y
我以前从来没有用过这样的monad,但是在想出这个例子之后,我可以看到它的优点。这将计算两个笛卡尔坐标之间的距离。它似乎实际上非常有用,因为它会自动将 Xs 上的任何操作与 Ys 上的任何操作分开。
collapsePair :: (a -> a -> b) -> Pair a -> b
collapsePair f (Pair x y) = f x y
type Coordinates = Pair Float
type Distance = Float
type TriangleSides = Pair Distance
-- Calculate the sides of a triangle given two x/y coordinates
triangleSides :: Coordinates -> Coordinates -> TriangleSides
triangleSides start end = do
-- Pair x1 y1
s <- start
-- Pair x2 y2
e <- end
-- Pair (x2 - x1) (y2 - y1)
Pair (e - s) (e - s)
-- Calculate the cartesian distance
distance :: Coordinates -> Coordinates -> Distance
distance start end = collapsePair distanceFormula (triangleSides start end)
where distanceFormula x y = sqrt (x ^ 2 + y ^ 2)
编辑:
原来这个例子可以只用 Pair
的 Applicative
实例来完成;不需要 Monad
:
import Control.Applicative
triangleSides :: Coordinates -> Coordinates -> TriangleSides
triangleSides = liftA2 (flip (-))
但是,我们可以通过在末尾的 X 上加 1,以人为的方式让它依赖于 Monad
:
triangleSides' :: Coordinates -> Coordinates -> TriangleSides
triangleSides' start end = do
s <- start
e <- end
Pair (e - s + 1) (e - s)
在这种情况下,最后一行 不能 转换为某种形式的 pure ...
因此必须使用 Monad 实例。
可以进一步探索的有趣之处在于,我们可以轻松地将其扩展为包括三维坐标(或更多)。我们可以使用 Representable
的默认实例,并通过 Data.Functor.Rep.
Co
新类型派生 Applicative
和 Monad
实例
然后我们可以将distance
计算抽象成它自己的类型类,它会在n维坐标上工作,只要我们为坐标类型写一个Distributive
实例。
{-# Language DeriveAnyClass #-}
{-# Language DeriveGeneric #-}
{-# Language DeriveTraversable #-}
{-# Language DerivingVia #-}
import Control.Applicative
import Data.Distributive
import Data.Functor.Rep
import GHC.Generics
class (Applicative c, Foldable c) => Coordinates c where
distance :: Floating a => c a -> c a -> a
distance start end = sqrt $ sum $ fmap (^2) $ liftA2 (-) end start
data Triple a = Triple
{ triple1 :: a
, triple2 :: a
, triple3 :: a
}
deriving ( Show, Eq, Ord, Functor, Foldable, Generic1, Representable
, Coordinates )
deriving (Applicative, Monad) via Co Triple
instance Distributive Triple where
distribute f = Triple (triple1 <$> f) (triple2 <$> f) (triple3 <$> f)
> distance (Triple 7 4 3) (Triple 17 6 2)
10.246950765959598
你可以验证这个答案here。
你的 Pair a
同构于 Reader Bool a
/Bool -> a
:
to (Pair f t) = \b -> if b then t else f
from f = Pair (f False) (f True)
因此,the Reader monad 的任何用例也是您的 monad 的潜在用例。这些数据类型的通用术语是 可表示仿函数。