【问题标题】:Known/estabilished usecases for the monad instance of an homogeneous pair同构对的单子实例的已知/已建立用例
【发布时间】:2021-03-13 00:40:18
【问题描述】:

有一次我问过this question,它被正确标记为this other one 的重复。

现在我有一个好奇心,对于一对同质类型的 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

【问题讨论】:

    标签: haskell monads


    【解决方案1】:

    您的Pair aReader Bool a/Bool -&gt; a 同构:

    to (Pair f t) = \b -> if b then t else f
    
    from f = Pair (f False) (f True)
    

    因此,the Reader monad 的任何用例也是您的 monad 的潜在用例。这些数据类型的通用术语是可表示的函子

    【讨论】:

    • 不过,PairMonad 实例与 Reader 不同。
    • @danidiaz 是吗?它看起来和我一样。
    • @Enlico 是的。这听起来像是与别人对你说的话相矛盾吗? (对我来说听起来不是这样。)
    • @danidiaz,对我来说也一样。
    • @danidiaz 再说一次,你为什么说它们似乎不相等?您能否提供一个具体案例说明它们何时表现不同?
    【解决方案2】:

    我以前从未使用过这样的 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)
    

    编辑:

    事实证明,这个例子可以只用ApplicativePair 实例来完成;不需要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 实例。


    可以进一步探索的有趣之处在于,我们可以轻松地将其扩展为包括 3 维坐标(或更多)。我们可以使用Representable 的默认实例并通过Data.Functor.Rep 中的Co 新类型派生ApplicativeMonad 实例。

    然后我们可以将 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

    【讨论】:

    • 距离公式来自 Pythagorean theorem,其中三角形的斜边 c 可以在给定边 ab 的情况下找到:a^2 + b^2 = c^2
    • 哦,好的。谢谢。但是,我认为您不需要 Monad 实例。它比 Applicative (-) &lt;$&gt; end &lt;*&gt; start 做了什么?实际上,您可以先使用(.-.) = liftA2 (-),然后再使用end .-. start,这样的可读性要强得多。 (顺便说一下,+1 的发明者!)
    • 这很好,这个例子可以用 Applicative 而不是 Monad 来完成。 (do 块的最后一行可能是pure (e - s)。)我想可能会有中间计算需要 Monad 用于其他(更好的)示例。
    • 虽然我们目前无法派生Distributive,但所有Representable函子都有一个默认定义:distribute = distributeRep
    • 在使用Applicative 拼写时:(&gt;&gt;=)(&lt;*&gt;) are equivalent for functions 和其他Representable 函子。既然如此,在这种情况下,总是可以转换为Applicative。例如,triangleSides' 可能被定义为Pair (\s e -&gt; s - e + 1) (\s e -&gt; s - e) &lt;*&gt; start &lt;*&gt; end。 (无论如何,这不会使您的示例无效-我确实喜欢 do-notation 在triangleSides 中的外观!)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2010-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多