aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2019-03-18 21:57:47 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2019-03-18 21:57:47 +0100
commitb4ac0fa66a3212fad3373a68d84d4b74c5fd309c (patch)
treeaecf6d89f36849e5efb999315c38d572cbbed48e
parentRemove entire examples directory. (diff)
downloadscheme-monad-b4ac0fa66a3212fad3373a68d84d4b74c5fd309c.tar.gz
scheme-monad-b4ac0fa66a3212fad3373a68d84d4b74c5fd309c.tar.xz
Wrote a bunch of documentation.
-rw-r--r--monad.scm81
-rw-r--r--monad/optional.scm10
-rw-r--r--monad/stack.scm13
-rw-r--r--monad/state.scm33
4 files changed, 87 insertions, 50 deletions
diff --git a/monad.scm b/monad.scm
index 97f4af6..9d21b4c 100644
--- a/monad.scm
+++ b/monad.scm
@@ -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)))
diff --git a/monad/optional.scm b/monad/optional.scm
index 015fc90..06c0e67 100644
--- a/monad/optional.scm
+++ b/monad/optional.scm
@@ -1,3 +1,10 @@
+;;; Commentary:
+;;
+;; Your classical optional (Maybe) monad.
+;; It has the constructors @code{just} and @code{nothing}.
+;;
+;;; Code:
+
(define-module (monad optional)
#:use-module (oop goops)
#:use-module (ice-9 match)
@@ -34,7 +41,8 @@ the value embedded in maybe-val"
default))
(define ((wrap-maybe proc) . values)
- "Wraps a function in an optional monad, where #f returns are translated to nothing."
+ "Wraps a function in an optional monad, where #f returns are translated to
+nothing."
(let ((v (apply proc values)))
(if v (just v) (nothing))))
diff --git a/monad/stack.scm b/monad/stack.scm
index 8d25303..fee48a6 100644
--- a/monad/stack.scm
+++ b/monad/stack.scm
@@ -1,14 +1,15 @@
+;;; Commentary:
+;; Simple stateful stack module for showing the state monad
+;; in action. These functions assume that they are in a
+;; (state list) monad. But dynamic types!
+;;
+;; TODO test these for empty stack
+;;; Code:
(define-module (monad stack)
#:export (pop peek push)
#:use-module (monad)
#:use-module (monad state))
-;;; Simple stateful stack module for showing the state monad
-;;; in action. These functions assume that they are in a
-;;; (state list) monad. But dynamic types!
-
-;;; TODO test these for empty stack
-
(define (pop)
(do st <- (get)
let top = (car st)
diff --git a/monad/state.scm b/monad/state.scm
index 471e756..40c9ff5 100644
--- a/monad/state.scm
+++ b/monad/state.scm
@@ -1,13 +1,26 @@
+;;; Commentary:
+;;
+;; The state monad!
+;; State is best modelled monadically as a function taking a state object, and
+;; returning another state object. This state object is in turn represented as i
+;; list where the first value is the last value returned, and the secound value
+;; is the internal state.
+;;
+;; All access to this internal value is done through the methods @code{get},
+;; @code{put}, and @code{modify}.
+;;
+;; One side effect of the @code{<state>} object not being directly accessible is
+;; that my trick for multiple dispatch return doesn't work. Which is why this
+;; modules also exports @code{return-state} directly.
+;;
+;;; Code:
+
(define-module (monad state)
#:use-module (oop goops)
#:use-module (ice-9 match)
#:use-module (monad)
#:export (return-state run-state get put modify)
- #:re-export (>>= >> fmap return))
-
-
-;; Alternative implementation of get.
-;; See https://hackage.haskell.org/package/mtl-2.2.1/docs/src/Control.Monad.State.Class.html#get
+ #:re-export (>>= >> return))
;;; newtype State = st-list -> st-list
@@ -48,10 +61,6 @@
((v _)
((proc (f v)) new-st-list)))))
-;; (define-stateful-method ((>> (a <state>) (b <state>)) st-list-a)
-;; (let ((st-list-b ((proc a) st-list-a)))
-;; ((proc b) st-list-b)))
-
(define-stateful ((return-state v) st-list)
"Sets the return value to v"
(cons v (cdr st-list)))
@@ -69,15 +78,11 @@
(list '() v))
(define-stateful ((modify proc) st-list)
+ "Applies proc to the value stored in state, and stores it back"
(match st-list
((r s)
(list '() (proc s)))))
-;; (define-stateful-method ((fmap (f <procedure>) (s <state>)) st-list)
-;; (match ((proc s) st-list)
-;; ((r st)
-;; (list (f r) st))))
-
(define-method (run-state (st <state>) init)
"Exec state with init as starting state value and st."
((proc st) (list init init)))