diff options
Diffstat (limited to '')
-rw-r--r-- | monad.scm | 81 |
1 files changed, 52 insertions, 29 deletions
@@ -1,44 +1,59 @@ +;;; Commentary: +;; +;; This is the base class of the monad interface. +;; It provides all the frames and glue required to use the library, and also +;; sets up the list monad (for multiple return values). +;; +;; To top it off, it gives default <top> definitions to return and bind (>>=), +;; meaning that they will sort of work with any type. +;; +;;; Code: + (define-module (monad) #:use-module (srfi srfi-1) #:use-module (ice-9 match) #:use-module (ice-9 curried-definitions) #:use-module (oop goops) #:replace (do) - #:export (sequence mapM - fmap <$> cmap <*> - >> >>= return)) + #:export (sequence + mapM return + <$> <*> + >> >>=)) + (define-generic return) -(define-method (return (a <top>)) identity) -(define-method (return (a <pair>)) list) -(define-generic >>=) +;; We start by defining our primitive operations, +(define-method (return (a <top>)) + "@code{return :: Monad m => a -> m a} +Since we can't directly defer type from context we instead allow @code{return} +to take an object of the desired type for @code{return}. + +The default implementation is simple the identity function. +" + identity) + +(define-generic >>=) (define-method (>>= (a <top>) (proc <procedure>)) - (proc a)) + "@code{bind :: Monad m => m a x (a -> m b) -> m b} -(define-method (>>= (this <null>) proc) '()) -(define-method (>>= (this <pair>) - (proc <procedure>)) - (concatenate! (map proc this))) +Bind (or >>=) takes a monad value along with a procedure taking a regular value +and returning a monad value. -(define-generic >>) +The default implementation simply applies proc to the value. Allowing any value +to be have the monadic type of being a scheme object. +" + (proc a)) +(define-generic >>) (define-method (>> (a <top>) (b <top>)) (>>= a (lambda args b))) -(define-method (>> (a <null>) (b <null>)) '()) -(define-method (>> (a <pair>) (b <null>)) '()) -(define-method (>> (a <null>) (b <pair>)) '()) -(define-method (>> (a <pair>) (b <pair>)) - (concatenate! (map (const b) a))) - -;; bind :: Monad m => m a -> (a -> m b) -> m b -;; return :: Monad m => a -> m a -;; map :: Functor f => (a -> b) -> f a -> f b - ;;; ---------------------------------------- +;;- We replace Scheme's built in @code{do} with our own, which works exactly like +;;- Haskell's do. @code{let} and @code{<-} included. (define-syntax do (syntax-rules (<- let =) ((_ let ptrn = val rest ...) @@ -52,16 +67,15 @@ ;;; ---------------------------------------- -(define (fmap f m) - (>>= m (lambda (x) ((return m) (f x))))) +(define (<$> f m_) + "@code{map :: Functor f => (a -> b) x f a -> f b} -(define <$> fmap) - -;; Curried map -(define (cmap f) - (lambda (m) (fmap f m))) +@code{Fmap}; works on any monadic type since all monads are monoids in the +category of @emph{endofunctors}@footnote{What's the problem?}" + (>>= m_ (lambda (m) ((return m_) (f m))))) (define (<*> f_ i_) + "@code{applicative :: Functor f => f (a -> b) x f a -> f b}" (do f <- f_ i <- i_ ((return f_) (f i)))) @@ -98,3 +112,12 @@ left to right, and collect the results. For a version that ignores the results see mapM_. https://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Monad.html#g:4" (sequence (map (lambda (x) (>>= x proc)) items))) + +;;; ---------------------------------------- + +(define-method (return (a <pair>)) list) + +(define-method (>>= (this <null>) proc) '()) +(define-method (>>= (this <pair>) + (proc <procedure>)) + (apply append (map proc this))) |