Created
December 7, 2011 16:40
-
-
Save paul-r-ml/1443519 to your computer and use it in GitHub Desktop.
simple reader monad
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'funkr/types' | |
class Reader | |
def initialize(func); @func = func; end | |
def run(env); @func.call(env); end | |
# Pour l'usage du foncteur applicatif | |
def map(&block); reader{|env| block.call(run(env))}; end | |
def self.pure(v); reader{|_env| v}; end | |
def apply(to); reader{|env| run(env).call(to.run(env))}; end | |
# Pour l'usage monadique | |
def bind(&block) | |
reader{|env| block.call(run(env)).run(env)} | |
end | |
# Quelques utilitaires | |
def self.ask; reader{|env| env }; end | |
def self.chain(&block); ask.bind(&block); end | |
def reader(&block); self.class.new(block); end | |
def self.reader(&block); self.new(block); end | |
end | |
JULIEN = Funkr::Types::SimpleRecord.new(name: "Julien", age: 25) | |
module ReaderMonadTest | |
def self.presentation | |
name_part.bind{|name| age_part.bind{|age| | |
Reader.pure( [name, ", ", age, "."].join ) | |
} } | |
end | |
def self.name_part | |
Reader.chain{|p| Reader.pure( "Bonjour je m'appelle " + p.name ) } | |
end | |
def self.age_part | |
Reader.chain{|p| Reader.pure( "et j'ai " + p.age.to_s + " ans") } | |
end | |
end | |
module ReaderApplicativeTest | |
def self.presentation | |
aconcat([name_part, Reader.pure(", "), age_part, Reader.pure(".")]) | |
end | |
def self.name_part | |
aconcat([Reader.pure("Bonjour je m'appelle "), Reader.ask.map(&:name)]) | |
end | |
def self.age_part | |
aconcat([Reader.pure("et j'ai "), Reader.ask.map{|p| p.age.to_s}, Reader.pure(" ans")]) | |
end | |
def self.aconcat(readers) | |
readers.inject{|a,e| a.map{|x| lambda{|y| x + y}}.apply(e)} | |
end | |
end | |
puts ReaderMonadTest.presentation.run(JULIEN) # => Bonjour je m'appelle Julien, et j'ai 25 ans. | |
puts ReaderApplicativeTest.presentation.run(JULIEN) # => Bonjour je m'appelle Julien, et j'ai 25 ans. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Le principe du reader peut être utilisé avec un simple foncteur applicatif, | |
-- qui est moins spécifique que les monades. Exemple : | |
import Control.Applicative | |
-- Un type qui sera une instance de Applicative. Il s'agit d'un simple | |
-- conteneur pour les fonctions d'un environnement vers un résultat. | |
newtype Reader env res = Reader (env -> res) | |
-- Une fonction qui prend un Reader et un environnement, et qui | |
-- renvoit le résultat | |
runReader :: Reader env res -> env -> res | |
runReader (Reader f) e = f e | |
instance Functor (Reader env) where | |
fmap g (Reader f) = Reader (g . f) | |
instance Applicative (Reader env) where | |
pure x = Reader $ const x | |
(Reader f) <*> (Reader x) = Reader $ \env -> (f env) (x env) | |
-- Obtenir le contenu de l'environnement comme résultat | |
ask :: Reader e e | |
ask = Reader id | |
-- Utilisation | |
data Person = Person { name :: String, age :: Int } | |
julien :: Person | |
julien = Person "Julien" 25 | |
-- affiche le résultat d'une fonction qui construit la présentation | |
main :: IO () | |
main = putStrLn $ show $ runReader presentation julien | |
presentation :: Reader Person String | |
presentation = aconcat [getNamePart, pure ", ", getAgePart] | |
getNamePart, getAgePart :: Reader Person String | |
getNamePart = aconcat [pure "Bonjour je m'appelle ", name <$> ask] | |
getAgePart = aconcat [pure "et j'ai ", (show . age) <$> ask, pure " ans"] | |
aconcat :: (Applicative ap) => [ap String] -> ap String | |
aconcat = foldl1 (liftA2 (++)) | |
-- Quand le programme est executé, il affiche "Bonjour je m'appelle | |
-- Julien, et j'ai 25 ans." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Un type qui sera une instance de Monad. Il s'agit d'un simple | |
-- conteneur pour les fonctions d'un environnement vers un résultat. | |
newtype Reader env res = Reader (env -> res) | |
-- Une fonction qui prend un Reader et un environnement, et qui | |
-- renvoit le résultat | |
runReader :: Reader env res -> env -> res | |
runReader (Reader f) e = f e | |
-- La fameuse instance de Monad. return ignore son environnement, | |
-- c'est en fait le conteneur de la fonction constante. bind execute | |
-- la fonction en lui fournissant l'environnement. | |
instance Monad (Reader env) where | |
return x = Reader $ \_ -> x | |
(Reader f) >>= g = Reader $ \env -> runReader (g $ f env) env | |
-- Obtenir le contenu de l'environnement comme résultat | |
ask :: Reader e e | |
ask = Reader id | |
-- Utilisation | |
data Person = Person { name :: String, age :: Int } | |
julien :: Person | |
julien = Person "Julien" 25 | |
-- affiche le résultat d'une fonction qui construit la présentation | |
main :: IO () | |
main = putStrLn $ show $ runReader presentation julien | |
-- tu m'as dit que tu aimais les "do" alors en voila ... | |
presentation :: Reader Person String | |
presentation = do | |
namePart <- getNamePart | |
agePart <- getAgePart | |
return $ concat [namePart, ", ", agePart, "."] | |
-- les sous-parties, sans le 'do' ce coup-ci pour changer | |
getNamePart, getAgePart :: Reader Person String | |
getNamePart = ask >>= \p -> | |
return $ concat ["Bonjour je m'appelle ", name p] | |
getAgePart = ask >>= \p -> | |
return $ concat ["et j'ai ", show $ age p, " ans"] | |
-- Quand le programme est executé, il affiche "Bonjour je m'appelle | |
-- Julien, et j'ai 25 ans." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
en ruby il y a deux moyens de construire un lambda.
Le premier moyen marche partout, et se fait avec le mot-clef lambda suivi d'un bloc paramétré. Par exemple lambda{|x,y| x+y}.
Le second moyen est un cas particulier pour les fonctions d'ordre supérieur qui nécessitent, de toute façon, toujours un lambda lors de l'appel. Dans ce cas, pour économiser à l'appelant l'effort de taper "lambda", le dernier paramètre de la fonction peut être noté &bidule, comme dans "def fonction(p1, p2, &bidule)". L'appelant pourra alors simplement taper fonction(v1,v2){|x| machin(x)}.
Typiquement : [1,2,3].map{|x| x+1} va donner [2,3,4].
Ce que j'en pense : c'est une bidouille peu générique mais commode, introduite pour rendre plus agréable l'usage des fonctions d'ordre sup. paramétrées par UNE fonction ano..