-
-
Save paul-r-ml/1443519 to your computer and use it in GitHub Desktop.
| 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. |
| -- 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." |
| -- 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." |
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..
Sympa :)
Ça fait quoi le
&dans la définition des paramètres pris par une fonction Ruby ?