【问题标题】:Specializing bind for monads over special typeclasses in Haskell在 Haskell 中专门为 monad 绑定特殊类型类
【发布时间】:2013-03-03 20:24:26
【问题描述】:

在非常好的教程“Learn You a Haskell for a Great Good”的倒数第二章 For a Few Monads More 中,作者定义了以下 monad:

import Data.Ratio  
newtype Prob a = Prob { getProb :: [(a,Rational)] } deriving Show
flatten :: Prob (Prob a) -> Prob a  
flatten (Prob xs) = Prob $ concat $ map multAll xs  
  where multAll (Prob innerxs,p) = map (\(x,r) -> (x,p*r)) innerxs
instance Monad Prob where  
  return x = Prob [(x,1%1)]  
  m >>= f = flatten (fmap f m)  
  fail _ = Prob []

我想知道是否有可能在 Haskell 中专门化绑定运算符“>>=”以防 monad 中的值属于像 Eq 这样的特殊类型类,因为我想将属于同一类的所有概率相加价值。

【问题讨论】:

    标签: haskell


    【解决方案1】:

    这称为“受限单子”,您可以这样定义它:

    {-# LANGUAGE ConstraintKinds, TypeFamilies, KindSignatures, FlexibleContexts, UndecidableInstances #-}
    module Control.Restricted (RFunctor(..),
                               RApplicative(..),
                               RMonad(..),
                               RMonadPlus(..),) where
    import Prelude hiding (Functor(..), Monad(..))
    import Data.Foldable (Foldable(foldMap))
    import GHC.Exts (Constraint)
    
    class RFunctor f where
        type Restriction f a :: Constraint
        fmap :: (Restriction f a, Restriction f b) => (a -> b) -> f a -> f b
    
    class (RFunctor f) => RApplicative f where
        pure :: (Restriction f a) => a -> f a
        (<*>) :: (Restriction f a, Restriction f b) => f (a -> b) -> f a -> f b
    
    class (RApplicative m) => RMonad m where
        (>>=) :: (Restriction m a, Restriction m b) => m a -> (a -> m b) -> m b
        (>>) :: (Restriction m a, Restriction m b)  => m a -> m b ->  m b
        a >> b = a >>= \_ -> b
        join :: (Restriction m a, Restriction m (m a)) => m (m a) -> m a
        join a = a >>= id
        fail :: (Restriction m a) => String -> m a
        fail = error
    
    return :: (RMonad m, Restriction m a) => a -> m a
    return = pure
    
    class (RMonad m) => RMonadPlus m where
        mplus :: (Restriction m a) => m a -> m a -> m a
        mzero :: (Restriction m a) => m a
        msum :: (Restriction m a, Foldable t) => t (m a) -> m a
        msum t = getRMonadPlusMonoid $ foldMap RMonadPlusMonoid t
    
    data RMonadPlusMonoid m a = RMonadPlusMonoid { getRMonadPlusMonoid :: m a }
    
    instance (RMonadPlus m, Restriction m a) => Monoid (RMonadPlusMonoid m a) where
        mappend (RMonadPlusMonoid x) (RMonadPlusMonoid y) = RMonadPlusMonoid $ mplus x y
        mempty = RMonadPlusMonoid mzero
        mconcat t = RMonadPlusMonoid . msum $ map getRMonadPlusMonoid t
    
    guard :: (RMonadPlus m, Restriction m a) => Bool -> m ()
    guard p = if p then return () else mzero
    

    要使用受限制的 monad,您需要像这样开始您的文件:

    {-# LANGUAGE ConstraintKinds, TypeFamilies, RebindableSyntax #-}
    module {- module line -} where
    import Prelude hiding (Functor(..), Monad(..))
    import Control.Restricted
    

    【讨论】:

    【解决方案2】:

    感谢 Ptharien 的 Flame 的回答(请投赞成票!)我设法改编了“Learn You a Haskell for a Great Good”运行的示例 monad。因为我不得不在谷歌上搜索一些细节(作为一个 Haskell 新手),所以这就是我最后所做的(“Learn ...”中的 FlipThree 示例现在给出 [(True,9 % 40), (False,31 % 40 )]):

    文件 Control/Restricted.hs(为了缩短它,我删除了 RApplicative、RMonadPlus 等):

    {-# LANGUAGE ConstraintKinds, TypeFamilies, KindSignatures, FlexibleContexts, UndecidableInstances #-}
    module Control.Restricted (RFunctor(..),
                               RMonad(..)) where
    import Prelude hiding (Functor(..), Monad(..))
    import Data.Foldable (Foldable(foldMap))
    import Data.Monoid
    import GHC.Exts (Constraint)
    
    class RFunctor f where
      type Restriction f a :: Constraint
      fmap :: (Restriction f a, Restriction f b) => (a -> b) -> f a -> f b
    
    class (RFunctor m) => RMonad m where
      return :: (Restriction m a) => a -> m a
      (>>=) :: (Restriction m a, Restriction m b) => m a -> (a -> m b) -> m b
      (>>) :: (Restriction m a, Restriction m b)  => m a -> m b -> m b
      a >> b = a >>= \_ -> b
      join :: (Restriction m a, Restriction m (m a)) => m (m a) -> m a
      join a = a >>= id
      fail :: (Restriction m a) => String -> m a
      fail = error
    

    文件 Prob.hs:

    {-# LANGUAGE ConstraintKinds, TypeFamilies, RebindableSyntax, FlexibleContexts #-}
    import Data.Ratio
    import Control.Restricted
    import Prelude hiding (Functor(..), Monad(..))
    import Control.Arrow (first, second)
    import Data.List (all)
    
    newtype Prob a = Prob { getProb :: [(a, Rational)] } deriving Show
    
    instance RFunctor Prob where
      type Restriction Prob a = (Eq a)
      fmap f (Prob as) = Prob $ map (first f) as
    
    flatten :: Prob (Prob a) -> Prob a
    flatten (Prob xs) = Prob $ concat $ map multAll xs
      where multAll (Prob innerxs, p) = map (\(x, r) -> (x, p*r)) innerxs
    
    compress :: Eq a => Prob a -> Prob a
    compress (Prob as) = Prob $ foldr f [] as
      where f a [] = [a]
            f (a, p) ((b, q):bs) | a == b    = (a, p+q):bs
                                 | otherwise = (b, q):f (a, p) bs
    
    instance Eq a => Eq (Prob a) where
      (==) (Prob as) (Prob bs) = all (`elem` bs) as
    
    instance RMonad Prob where
      return x = Prob [(x, 1%1)]
      m >>= f = compress $ flatten (fmap f m)
      fail _ = Prob []
    

    【讨论】:

      【解决方案3】:

      这里另一种可能性基于使用technique by Ganesh Sittampalam 的广义代数数据类型:

      {-# LANGUAGE GADTs #-}
      
      import Control.Arrow (first, second)
      import Data.Ratio
      import Data.List (foldl')
      
      -- monads over typeclass Eq
      class EqMonad m where
        eqReturn :: Eq a => a -> m a
        eqBind :: (Eq a, Eq b) => m a -> (a -> m b) -> m b
        eqFail :: Eq a => String -> m a
        eqFail = error
      
      data AsMonad m a where
        Embed :: (EqMonad m, Eq a) => m a -> AsMonad m a
        Return :: EqMonad m => a -> AsMonad m a
        Bind :: EqMonad m => AsMonad m a -> (a -> AsMonad m b) -> AsMonad m b
      
      instance EqMonad m => Monad (AsMonad m) where
        return = Return
        (>>=) = Bind
        fail = error
      
      unEmbed :: Eq a => AsMonad m a -> m a
      unEmbed (Embed m) = m
      unEmbed (Return v) = eqReturn v
      unEmbed (Bind (Embed m) f) = m `eqBind` (unEmbed . f)
      unEmbed (Bind (Return v) f) = unEmbed (f v)
      unEmbed (Bind (Bind m f) g) = unEmbed (Bind m (\x -> Bind (f x) g))
      
      -- the example monad from "Learn you a Haskell for a Great good"
      newtype Prob a = Prob { getProb :: [(a, Rational)] }
        deriving Show
      
      instance Functor Prob where
        fmap f (Prob as) = Prob $ map (first f) as
      
      flatten :: Prob (Prob a) -> Prob a
      flatten (Prob xs) = Prob $ concat $ map multAll xs
        where multAll (Prob innerxs, p) = map (\(x, r) -> (x, p*r)) innerxs
      
      compress :: Eq a => Prob a -> Prob a
      compress (Prob as) = Prob $ foldl' f [] as
        where f [] a = [a]
              f ((b, q):bs) (a, p) | a == b    = (a, p+q):bs
                                   | otherwise = (b, q):f bs (a, p)
      
      instance Eq a => Eq (Prob a) where
        (==) (Prob as) (Prob bs) = all (`elem` bs) as
      
      instance EqMonad Prob where
        eqReturn x = Prob [(x, 1%1)]
        m `eqBind` f = compress $ flatten (fmap f m)
        eqFail _ = Prob []
      
      newtype Probability a = Probability { getProbability :: AsMonad Prob a }
      
      instance Monad Probability where
        return = Probability . Return
        a >>= f = Probability $ Bind (getProbability a) (getProbability . f)
        fail = error
      
      instance (Show a, Eq a) => Show (Probability a) where
        show = show . getProb . unEmbed . getProbability
      
      -- Example flipping four coins (now as 0/1)
      prob :: Eq a => [(a, Rational)] -> Probability a
      prob = Probability . Embed . Prob
      
      coin :: Probability Int
      coin = prob [(0, 1%2), (1, 1%2)]
      
      loadedCoin :: Probability Int
      loadedCoin = prob [(0, 1%10), (1, 9%10)]
      
      flipFour :: Probability Int
      flipFour = do
        a <- coin
        b <- coin
        c <- coin
        d <- loadedCoin
        return (a+b+c+d)
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-27
      • 1970-01-01
      相关资源
      最近更新 更多