[Haskell] Параметризованные монады имени Эдуарда Кметта
От: Паблик Морозов  
Дата: 02.12.11 15:23
Оценка: 36 (2)
[Haskell]] Параметризованные монады имени Эдуарда Кметта

Те, кто в теме, go to последний абзац -->

Рано или поздно у каждого монадовода возникает желание пополнить свою коллекцию параметризованной монадой (вот живой пример:

http://thesz.livejournal.com/1075518.html). Этот вид монад примечателен тем, что операторы в нём помимо типа монады m и

значения a несут в себе пред и пост условия, закодированные типами, что позволяет ограничивать возможные варианты связывания.

Выглядит параметризованная мондада примерно так:
class PMonad m where
    unit :: a -> m p p a
    bind :: m p q a -> (a -> m q r b) -> m p r b

Здесь видно, что, оператор bind биндит только монады с совпадающими пред и пост условиями (т.е. m p q к m q r и

получает в результате m p r). В качестве m обычно трансформер поверх IO, либо поверх StateT или ReaderT, которые в

свою очередь надеты на IO. пред и пост условия от
ражают требования, которые должны выполняться перед вызовом действия IO и состояние

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

освободите заблокированный ресурс (если не словите deadlock). Например как-то так:
-- | здесь l - lock, m - мондада, p q - пред и пост условия, a - возвращаемое значение.
newtype LockT l m p q a 

-- | Фантомные типы, отображающие состояния блокируемого ресурса: Unlocked - разблокирован, Shared - разделяемая блокировка 

(чтение), Exclusive - эксклюзивная блокировка (запись)
data Unlocked
data Shared
data Exclusive

-- | Получает блокировку на чтение
acquire :: LockT l IO Unlocked Shared ()

-- | Поднимает уровень блокировки с разделяемого до эксклюзивного
raise :: LockT l IO Shared Exclusive ()

-- | Понижает уровень блокировки
lower :: LockT l IO Exclusive Shared ()

-- | Снимает блокировку
release :: LockT l IO Shared Unlocked ()

-- | выполняет последовательность действий с блокируемым ресурсом
withLockT :: l -> (forall l' . LockT l' IO Unlocked Unlocked a) -> a

Последовательность действий может быть запущена только с помощью функции withLockT, которая принимает последовательности, стартующие с разблокированным ресурсом и снимающие блокировку перед завершением. Внутри они могут сколько угодно раз брать, снимать, повышать и понижать блокировку, но только в правильной последовательности. Функция требует от монады быть полиморфной по l', чтобы не было возможности "вытащить" действие наружу (например, написать функцию типа :: LockT <concreteType> IO Shared Unlocked (LockT <concreteType> IO Shared Unlocked)) и вызвать его в другой цепочке, не относящейся к данному ресурсу. Казалось бы, какая замечательная монада

--> тот самый последний абзац

но засада заключается в том, что поиск ванильной параметризованной монады по хакаджу не даёт результатов. Вместо неё находится мутировавший вариант — параметризованная монада имени Эдуарда Кметта (http://comonad.com/reader/2007/parameterized-monads-in-haskell/). Вопрос монадоводам: чем, собственно, она лучше, использовал ли кто-нибудь, какие подводные камни?
Re: [Haskell] lifted-base
От: Паблик Морозов  
Дата: 05.12.11 13:43
Оценка:
Решил я, что этот раздел достаточно тихий, чтобы исползовать его в качестве своего блога.
----

Дорогой дневник, продолжая свои исследования в области монадологии (http://ru.wikipedia.org/wiki/Монадология) я заметил за собой некоторое желание не привязываться к IO, а использовать MonadIO или что-нибудь подобное. Особенно сильно желание даёт о себе знать при активном использовании трансформеров. А так как Хаскель придман специально для программистов, идущих на поводу у своих желаний, то почему бы и нет?

Проблема: функции типа :: (a -> IO b) -> IO b. На деревенском это callback-функции выполняющий действие в IO. Например: mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b, withMVar :: MVar a -> (a -> IO b) -> IO b (и прочие варианты with...)

Наивная реализация ведет к появлению классов вида
class MyClass m where
   withSomething :: Data' -> (Data -> m b) -> m b

и экземпляров вида
instance (MonadIO m) => MyClass (MyCrazyTransformer m) where
   withSomething :: Data' -> (Data -> (MyCrazyTransformer m) b) -> (MyCrazyTransformer m) b

Но имея на руках только MonadIO преобразовать (Data -> (MyCrazyTransformer m) b) -> (MyCrazyTransformer m) b к (Data -> IO b) -> IO b невозможно. На помощь приходит гуголь и выясняется, что данная проблема уже обсуждалась (начиная примерно отсюда http://www.haskell.org/pipermail/libraries/2010-April/013485.html), с февраля этого года Bas van Dijk и Anders Kaseorg выкатывают http://hackage.haskell.org/package/monad-control-0.3.0.1 (осторожно, моск!), а буквально за два дня до выборов в гос.думу заливают на хакажд http://hackage.haskell.org/package/lifted-base-0.1.0.1 с генерализованными IO функциями из Concurrent, MVar, Exception и Timeout. Вот такие бывают в жизни совпадения.
Re: [Haskell] monad-control-0.3.0.1
От: Паблик Морозов  
Дата: 09.12.11 13:35
Оценка:
{-# LANGUAGE GeneralizedNewtypeDeriving
           , TypeFamilies
           , UndecidableInstances #-}

module Spice.Concurrent.MVar where

import           Control.Applicative
import qualified Control.Concurrent.MVar as M
import qualified Control.Concurrent.MVar.Lifted as L
import           Control.Monad
import           Control.Monad.Base
import           Control.Monad.Trans.Class 
import           Control.Monad.Trans.Control
import           Control.Monad.Trans.Identity

class (Monad m) => MVarMonad m where
    type MVarType m :: * -> *

    takeMVar :: MVarType m a -> m a
    putMVar  :: MVarType m a -> a -> m ()
    modifyMVar :: MVarType m a -> (a -> m (a, b)) -> m b
  
newtype MVarT m a = MVarT { unMVarT :: IdentityT m a }
    deriving ( Applicative
             , Functor
             , Monad
             , MonadTrans
             , MonadBase b )

instance (MonadBase IO m) => MVarMonad (MVarT m) where
    type MVarType (MVarT m) = M.MVar

    takeMVar   = L.takeMVar
    putMVar    = L.putMVar
    -- modifyMVar = L.modifyMVar requires (MonadBaseControl IO (MVarT m))

MonadBaseControl почему-то автоматически не наследуется. Что в ней такого особенного?
Re[2]: [Haskell] monad-control-0.3.0.1
От: Паблик Морозов  
Дата: 09.12.11 13:42
Оценка:
Неужели дело только в наличии associated data family у MonadBaseControl?
Re[2]: [Haskell] monad-control-0.3.0.1
От: Паблик Морозов  
Дата: 09.12.11 14:39
Оценка:
Если сделать MonadBaseControl ручками, то всё работает
{-# LANGUAGE GeneralizedNewtypeDeriving
           , MultiParamTypeClasses
           , TypeFamilies
           , UndecidableInstances
           , KitchenSink #-}

module Spice.Concurrent.MVar where

import           Control.Applicative
import qualified Control.Concurrent.MVar as M
import qualified Control.Concurrent.MVar.Lifted as L
import           Control.Monad
import           Control.Monad.Base
import           Control.Monad.Trans.Class 
import           Control.Monad.Trans.Control
import           Control.Monad.Trans.Identity

class (Monad m) => MVarMonad m where
    type MVar m :: * -> *

    takeMVar :: MVar m a -> m a
    putMVar  :: MVar m a -> a -> m ()
    modifyMVar :: MVar m a -> (a -> m (a, b)) -> m b
  
newtype MVarMonadT m a = MVarT { unMVarT :: IdentityT m a }
    deriving ( Applicative
             , Functor
             , Monad
             , MonadTrans
             , MonadBase b )
             
runMVarT = runIdentityT . unMVarT
             
instance (MonadBaseControl IO m) => MonadBaseControl IO (MVarMonadT m) where
    newtype StM (MVarMonadT m) a = StMVarMonad { unStMVarMonad :: StM (IdentityT m) a }
    
    liftBaseWith f = MVarT $ liftBaseWith $ \run -> f $ liftM StMVarMonad . run . unMVarT
    restoreM = MVarT . restoreM . unStMVarMonad

instance (MonadBase IO m, MonadBaseControl IO m) => MVarMonad (MVarMonadT m) where
    type MVar (MVarMonadT m) = M.MVar

    takeMVar   = L.takeMVar
    putMVar    = L.putMVar
    modifyMVar = L.modifyMVar

Многовато говна получается для такой тривиальной задачи.
Re: [Haskell] Еще одна статья, объясняющая что такое монады
От: Паблик Морозов  
Дата: 27.12.11 15:56
Оценка: 60 (1)
http://blog.sigfpe.com/2006/06/monads-kleisli-arrows-comonads-and.html

а что, мну нра
Re: [Haskell] Конструктивная Алгебра имени Эдуарда Кметта
От: Паблик Морозов  
Дата: 29.12.11 10:49
Оценка:
Рано или поздно у любого нормального человека, вроде физика или математика, возникает желание использовать нормальные алгебраические структуры, вроде абелевых полугрупп или унитарных алгебр над идемпотентными полукольцами. Обычно в таких случаях предлагают воспользоваться http://hackage.haskell.org/package/numeric-prelude, но блин, какая-то она "не такая".. Возможно это из-за того, что все тайпклассы там называются C, а типы данных T, короче обычные люди привыкли программировать на Хаскелле немного не так. Специально для них Эдуардом Кметтом написана http://hackage.haskell.org/package/algebra, как обычно требующая {-# LANGUAGE KitchenSink #-} для сборки, но в целом, более привычная. Ммм.. орбит-абстрактная алгебра с ароматом абстрактной алгебры.
Re[2]: [Haskell] Еще одна статья, объясняющая что такое мона
От: deniok Россия  
Дата: 30.12.11 15:50
Оценка:
Здравствуйте, Паблик Морозов, Вы писали:

ПМ>http://blog.sigfpe.com/2006/06/monads-kleisli-arrows-comonads-and.html


ПМ>а что, мну нра


Да, хорошая статья, хотя довольно старая и поэтому местами out-of-date; например, композиция стрелок Клейсли в Control.Monad давно есть, это оператор "рыбка" <span class='lineQuote level1'>&gt;=&gt;</span>.
Re[3]: [Haskell] Еще одна статья, объясняющая что такое мона
От: Паблик Морозов  
Дата: 30.12.11 16:44
Оценка:
Здравствуйте, deniok, Вы писали:

D>Да, хорошая статья, хотя довольно старая и поэтому местами out-of-date; например, композиция стрелок Клейсли в Control.Monad давно есть, это оператор "рыбка" <span class='lineQuote level1'>&gt;=&gt;</span>.


Я даже не заметил сначала, что в статье написано, что такой операции нет :3
Re: [Haskell] Iteratees
От: Паблик Морозов  
Дата: 10.01.12 11:36
Оценка: 1 (1)
Новогодние праздники замедлили моё знакомство с сonduits, в общем я пока еще ничего с ними не делал. Зато нашел статью про Iteratees Эдуарда Янга: http://blog.ezyang.com/2012/01/why-iteratees-are-hard-to-understand/

Iteratees, конечно, я использовать не буду, но статья хорошая.
Re: Рекурсия с эффектом
От: Паблик Морозов  
Дата: 18.01.12 18:25
Оценка:
В продолжение того, о чём я начал писать здесь: http://rsdn.ru/forum/decl/4577783.1.aspx
Автор: Паблик Морозов
Дата: 16.01.12

{-# LANGUAGE TypeSynonymInstances #-}

module Yoba where

import           Control.Monad.Identity hiding (fix)

-- fix

newtype FixF f a = FixF { unFixF :: f (a (FixF f a)) }

fixF = FixF

-- base

data ListB a r = Empty | Cons a r deriving (Show)

-- pure

type Fix = FixF Identity

fix :: a (FixF Identity a) -> FixF Identity a
fix = fixF . Identity 

unFix :: FixF Identity a -> a (FixF Identity a)
unFix = runIdentity . unFixF

type List a = (Fix (ListB a))

instance (Show a) => Show (List a) where
    show a = '(' : shows (unFix a) ")"

fromList :: [Int] -> List Int
fromList [] = fix Empty
fromList (x:xs) = fix $ Cons x $ fromList xs

-- io

type FixIO = FixF IO

fixIO :: a (FixF IO a) -> FixF IO a
fixIO = fixF . return

unFixIO :: FixF IO a -> a (FixF IO a)
unFixIO = error "impossible (requires unsafePerformIO)"

type ListIO a = (FixIO (ListB a))

fromConsole :: ListIO Int
fromConsole = fixF $ do
    l <- getLine
    if null l then return Empty
              else return $ Cons (read l) fromConsole

toConsole :: ListIO Int -> IO ()
toConsole list = do
    a <- unFixF list 
    case a of
        Empty -> return ()
        Cons x xs -> do
            putStrLn $ show x
            toConsole xs

Рекурсивные типы с эффектом (генерализованный катаморфизм потом напишу). Обычный рекурсивный тип — частный случай при f = Identity.
Re: [Haskell] Рекурсия и конечные потоки.
От: Паблик Морозов  
Дата: 31.01.12 07:50
Оценка:
Для библиотек streams + recursion-schemes:

import Data.Functor.Foldable
import Data.Stream.Future

data instance Prim (Future a) b = FutureP a b | LastP a deriving (Eq, Ord, Show, Read)

instance Functor (Prim (Future a)) where
    fmap f (FutureP a b) = FutureP a (f b)
    fmap _ (LastP a) = (LastP a)

type instance Base (Future a) = Prim (Future a)

instance Foldable (Future a) where
    project (x :< xs) = FutureP x xs
    project (Last x) = LastP x

instance Unfoldable (Future a) where
    embed (FutureP x xs) = x :< xs
    embed (LastP a) = Last a


Не знаю, насколько это корректно, и не хочу разбираться, поэтому отправил письмо Кметту. Para и apo реализовывать не стал. Самому мне нужно было для ana, а конечный поток я использую в качестве непустого списка. Зачем нужны непустые списки, если есть конечные потоки? Или зачем нужны конечные потоки, если есть непустые списки?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.