5

最後の 2 番目の章For a Few Monadsの非常に優れたチュートリアル「Learn You a Haskell for a Great Good」で、著者は次のモナドを定義しています。

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 []

同じ値に属するすべての確率を合計したいので、モナドの値が Eq のような特別な型クラスに属している場合に備えて、Haskell でバインド演算子 ">>=" を特殊化できるかどうか疑問に思いました。

4

3 に答える 3

10

これは「制限付きモナド」と呼ばれ、次のように定義します。

{-# 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

制限されたモナドを使用するには、次のようにファイルを開始する必要があります。

{-# LANGUAGE ConstraintKinds, TypeFamilies, RebindableSyntax #-}
module {- module line -} where
import Prelude hiding (Functor(..), Monad(..))
import Control.Restricted
于 2013-03-03T20:50:37.810 に答える
1

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)
于 2013-03-04T16:57:57.743 に答える
1

Ptharien の Flame の回答のおかげで (賛成票を投じてください!)、実行中の「Haskell を学ぶ」のモナドの例をうまく適応させることができました。私はいくつかの詳細をグーグルで調べなければならなかったので(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 []
于 2013-03-04T14:52:01.740 に答える