Существует ли монада Powerset-over-Reader?

Канонический "экземпляр Монады" для совместного использования среды плюс недетерминизм выглядит следующим образом (с использованием псевдо-Хаскелла, так как Хаскелл Data.Set не, конечно, монадный)

eta :: a -> r -> {a} -- '{a}' means the type of a set of a's
eta x = \r -> {x}

bind :: (r -> {a}) -> (a -> r -> {b}) -> r -> {b}
m `bind` f = \r -> {v | x ∈ m r, v ∈ f x r}

Обычно при попытке объединить монаду "контейнер", такую ​​как Powerset (List, Writer и т. Д.), Со второй монадой m (здесь, примерно, Reader), один "обертывания" m вокруг контейнера монада, как сделано выше.

Интересно тогда о следующей потенциальной спецификации Powerset-over-Reader:

eta' :: a -> {r -> a}
eta' x = {\r -> x}

bind' :: {r -> a} -> (a -> {r -> b}) -> {r -> b}
m `bind'` f = {rb | x <- m, ∀r: ∃rb' ∈ f (x r): rb r == rb' r}

Это не кажется сумасшедшим (я понимаю, что GHCi не может проверить rb r == rb' r для многих rb а также rb'), но bind' достаточно сложен, чтобы затруднить (для меня) проверку соблюдения законов монады.

Тогда мой вопрос: eta' а также bind' действительно являются монадическими - и, если нет, какой из законов нарушается, и какому непредвиденному поведению это может соответствовать?

Вторичный вопрос, предполагая, что eta' а также bind' не монадические, как можно определить , существуют ли функции с этими типами?

2 ответа

Решение

Веселый вопрос. Вот мое мнение - давайте посмотрим, если я никуда не лох!

Для начала я напишу ваши подписи на (чуть менее псевдо) языке Haskell:

return :: a -> PSet (r -> a)
(>>=) :: PSet (r -> a) -> (a -> PSet (r -> b)) -> PSet (r -> b))

Прежде чем продолжить, стоит упомянуть два практических осложнения. Во-первых, как вы уже заметили, благодаря Eq и / или Ord ограничения, нетривиально давать множества Functor или же Monad экземпляры; в любом случае, есть способы обойти это. Во-вторых, и что еще более тревожно, с типом, который вы предлагаете для (>>=) надо извлечь a с из PSet (r -> a) без каких-либо очевидных поставок r s - или, другими словами, ваш (>>=) требует обхода функтора функции (->) r, Это, конечно, невозможно в общем случае и имеет тенденцию быть непрактичным даже тогда, когда это возможно - по крайней мере, в отношении Хаскелла. В любом случае, для наших умозрительных целей можно предположить, что мы можем пройти (->) r применяя функцию ко всем возможным r ценности. Я укажу это через волнистые universe :: PSet r набор, названный в честь этого пакета. Я также буду использовать universe :: PSet (r -> b)и предположим, что мы можем сказать, два ли r -> b функции согласуются на определенную r даже не требуя Eq ограничение. (Псевдо-Хаскель становится действительно фальшивым!)

Предварительные замечания, вот мои псевдо-Haskell версии ваших методов:

return :: a -> PSet (r -> a)
return x = singleton (const x)

(>>=) :: PSet (r -> a) -> (a -> PSet (r -> b)) -> PSet (r -> b))
m >>= f = unionMap (\x ->
    intersectionMap (\r ->
        filter (\rb -> 
            any (\rb' -> rb' r == rb r) (f (x r)))
            (universe :: PSet (r -> b)))
        (universe :: PSet r)) m
    where
    unionMap f = unions . map f
    intersectionMap f = intersections . map f

Далее законы монады:

m >>= return = m
return y >>= f = f y
m >>= f >>= g = m >>= \y -> f y >>= g

(Кстати, когда вы делаете такие вещи, хорошо иметь в виду другие презентации класса, с которым мы работаем - в этом случае мы имеем join а также (>=>) в качестве альтернативы (>>=) - поскольку переключение презентаций может сделать работу с выбранным вами экземпляром более приятной. Здесь я буду придерживаться (>>=) презентация Monad.)

Вперед к первому закону...

m >>= return = m
m >>= return -- LHS
unionMap (\x ->
    intersectionMap (\r ->
        filter (\rb -> 
            any (\rb' -> rb' r == rb r) (singleton (const (x r))))
            (universe :: PSet (r -> b)))
        (universe :: PSet r)) m
unionMap (\x ->
    intersectionMap (\r ->
        filter (\rb -> 
            const (x r) r == rb r)
            (universe :: PSet (r -> b)))
        (universe :: PSet r)) m
unionMap (\x ->
    intersectionMap (\r ->
        filter (\rb -> 
            x r == rb r)
            (universe :: PSet (r -> b)))
        (universe :: PSet r)) m
-- In other words, rb has to agree with x for all r. 
unionMap (\x -> singleton x) m
m -- RHS

Один вниз, два, чтобы идти.

return y >>= f = f y
return y -- LHS
unionMap (\x ->
    intersectionMap (\r ->
        filter (\rb -> 
            any (\rb' -> rb' r == rb r) (f (x r)))
            (universe :: PSet (r -> b)))
        (universe :: PSet r)) (singleton (const y))
(\x ->
    intersectionMap (\r ->
        filter (\rb -> 
            any (\rb' -> rb' r == rb r) (f (x r)))
            (universe :: PSet (r -> b)))
        (universe :: PSet r)) (const y)
intersectionMap (\r ->
    filter (\rb -> 
        any (\rb' -> rb' r == rb r) (f (const y r)))
        (universe :: PSet (r -> b)))
    (universe :: PSet r)
intersectionMap (\r ->
    filter (\rb -> 
        any (\rb' -> rb' r == rb r) (f y)))
        (universe :: PSet (r -> b)))
    (universe :: PSet r)
-- This set includes all functions that agree with at least one function
-- from (f y) at each r.

return y >>= f поэтому, возможно, может быть гораздо большим набором, чем f y, У нас есть нарушение второго закона; следовательно, у нас нет монады - по крайней мере, не с предложенным здесь примером.


Приложение: здесь приведена актуальная работоспособная реализация ваших функций, которую можно использовать, по крайней мере, для игры с мелкими шрифтами. Он использует преимущества вышеупомянутого пакета юниверса.

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
module FunSet where

import Data.Universe
import Data.Map (Map)
import qualified Data.Map as M
import Data.Set (Set)
import qualified Data.Set as S
import Data.Int
import Data.Bool

-- FunSet and its would-be monad instance

newtype FunSet r a = FunSet { runFunSet :: Set (Fun r a) }
    deriving (Eq, Ord, Show)

fsreturn :: (Finite a, Finite r, Ord r) => a -> FunSet r a
fsreturn x = FunSet (S.singleton (toFun (const x)))

-- Perhaps we should think of a better name for this...
fsbind :: forall r a b.
    (Ord r, Finite r, Ord a, Ord b, Finite b, Eq b)
    => FunSet r a -> (a -> FunSet r b) -> FunSet r b
fsbind (FunSet s) f = FunSet $
    unionMap (\x ->
        intersectionMap (\r ->
            S.filter (\rb ->
                any (\rb' -> funApply rb' r == funApply rb r)
                    ((runFunSet . f) (funApply x r)))
                (universeF' :: Set (Fun r b)))
            (universeF' :: Set r)) s

toFunSet :: (Finite r, Finite a, Ord r, Ord a) => [r -> a] -> FunSet r a
toFunSet = FunSet . S.fromList . fmap toFun

-- Materialised functions

newtype Fun r a = Fun { unFun :: Map r a }
    deriving (Eq, Ord, Show, Functor)

instance (Finite r, Ord r, Universe a) => Universe (Fun r a) where
    universe = fmap (Fun . (\f ->
        foldr (\x m ->
            M.insert x (f x) m) M.empty universe))
        universe

instance (Finite r, Ord r, Finite a) => Finite (Fun r a) where
    universeF = universe

funApply :: Ord r => Fun r a -> r -> a
funApply f r = maybe
    (error "funApply: Partial functions are not fun")
    id (M.lookup r (unFun f))

toFun :: (Finite r, Finite a, Ord r) => (r -> a) -> Fun r a
toFun f = Fun (M.fromList (fmap ((,) <$> id <*> f) universeF))

-- Set utilities

unionMap :: (Ord a, Ord b) => (a -> Set b) -> (Set a -> Set b)
unionMap f = S.foldl S.union S.empty . S.map f

-- Note that this is partial. Since for our immediate purposes the only
-- consequence is that r in FunSet r a cannot be Void, I didn't bother
-- with making it cleaner.
intersectionMap :: (Ord a, Ord b) => (a -> Set b) -> (Set a -> Set b)
intersectionMap f s = case ss of
    [] -> error "intersectionMap: Intersection of empty set of sets"
    _ -> foldl1 S.intersection ss
    where
    ss = S.toList (S.map f s)

universeF' :: (Finite a, Ord a) => Set a
universeF' = S.fromList universeF

-- Demo

main :: IO ()
main = do
    let andor = toFunSet [uncurry (&&), uncurry (||)]
    print andor -- Two truth tables
    print $ funApply (toFun (2+)) (3 :: Int8) -- 5
    print $ (S.map (flip funApply (7 :: Int8)) . runFunSet)
        (fsreturn (Just True)) -- fromList [Just True]
    -- First monad law demo
    print $ fsbind andor fsreturn == andor -- True
    -- Second monad law demo
    let twoToFour = [ bool (Left False) (Left True)
                    , bool (Left False) (Right False)]
        decider b = toFunSet
            (fmap (. bool (uncurry (&&)) (uncurry (||)) b) twoToFour)
    print $ fsbind (fsreturn True) decider == decider True -- False (!)

Несколько проще проверить законы в нотации Клейсли.

kleisli' :: (a -> {r -> b}) -> (b -> {r -> c}) -> (a -> {r -> c})
g `kleisli'` f = \z -> {rb | x <- g z, ∀r: ∃rb' ∈ f (x r): rb r == rb' r}

Попробуем проверить return `kleisli'` f = f,

(\a -> {\r->a}) `kleisli'` f = 
\z -> {rb | x <- {\r->z}, ∀r: ∃rb' ∈ f (x r): rb r == rb' r} = 
\z -> {rb | ∀r: ∃rb' ∈ f z: rb r == rb' r}

Скажи все наши типы a, b, c а также r являются Integer а также f x = {const x, const -x}, Какие функции в (return `kleisli'` f) 5? Этот набор должен быть f 5, то есть, {const 5, const -5},

Это? естественно const 5 а также const -5 оба в, но не только. Например, \r->if even r then 5 else -5 также в.

Другие вопросы по тегам