forked from coalton-lang/coalton
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfunctions.lisp
203 lines (166 loc) · 5.72 KB
/
functions.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
(coalton-library/utils:defstdlib-package #:coalton-library/functions
(:use
#:coalton
#:coalton-library/builtin
#:coalton-library/classes)
(:export
#:trace
#:traceObject
#:print
#:unsafe-pointer-eq?
#:fix
#:id
#:const
#:flip
#:reduce
#:compose
#:conjoin
#:disjoin
#:complement
#:curry
#:uncurry
#:pair-with
#:msum
#:asum
#:/=
#:bracket))
(in-package #:coalton-library/functions)
(named-readtables:in-readtable coalton:coalton)
#+coalton-release
(cl:declaim #.coalton-impl/settings:*coalton-optimize-library*)
(coalton-toplevel
(declare trace (String -> Unit))
(define (trace str)
"Print a line to `cl:*standard-output*`."
(progn
(lisp :a (str) (cl:format cl:t "~A~%" str))
Unit))
(declare traceObject (String -> :a -> Unit))
(define (traceObject str item)
"Print a line to `cl:*standard-output*` in the form \"{STR}: {ITEM}\"."
(progn
(lisp :a (str item) (cl:format cl:t "~A: ~A~%" str item))
Unit))
(declare print ((Into :a String) => :a -> Unit))
(define (print item)
"Print the String representation of `item` to `cl:*standard-output*`."
(trace (into item)))
(declare unsafe-pointer-eq? (:a -> :a -> Boolean))
(define (unsafe-pointer-eq? a b)
(lisp Boolean (a b)
(to-boolean (cl:eq a b))))
;;
;; Function combinators
;;
(declare fix (((:a -> :b) -> (:a -> :b)) -> (:a -> :b)))
(define (fix f n)
"Compute the fixed point of a unary function. This is equivalent to the Y-combinator of the lambda calculus. This combinator allows recursion without specific assignment of names. For example, the factorial function can be written
(define fact
(fix
(fn (f n)
(if (== n 0)
1
(* n (f (- n 1)))))))"
(f (fix f) n))
(declare id (:a -> :a))
(define (id x)
"A function that always returns its argument."
x)
(declare const (:a -> :b -> :a))
(define (const a _b)
"A function that always returns its first argument."
a)
(declare flip ((:a -> :b -> :c) -> :b -> :a -> :c))
(define (flip f x y)
"Returns a function that takes its arguments in reverse order."
(f y x))
(declare reduce (Foldable :f => (:a -> :b -> :b) -> :b -> (:f :a) -> :b))
(define (reduce f y xs)
"The same as `fold` but with the argument order swapped to match `cl:reduce`"
(fold (flip f) y xs))
;; We don't write (COMPOSE F G X) even though it's OK so that the
;; most common case of using compose---as a binary function---is
;; considered to be "saturated".
(declare compose ((:b -> :c) -> (:a -> :b) -> (:a -> :c)))
(define (compose f g)
"Produces a function equivalent to applying `g` followed by `f`."
;; Note: ((compose f g) x) behaves like (f (g x))
(fn (x)
(f (g x))))
(declare conjoin ((:a -> Boolean) -> (:a -> Boolean) -> :a -> Boolean))
(define (conjoin f g x)
"Compute the conjunction of two unary Boolean functions."
(and (f x) (g x)))
(declare disjoin ((:a -> Boolean) -> (:a -> Boolean) -> :a -> Boolean))
(define (disjoin f g x)
"Compute the disjunction of two unary Boolean functions."
(or (f x) (g x)))
(declare complement ((:a -> Boolean) -> :a -> Boolean))
(define (complement f x)
"Compute the complement of a unary Boolean function."
(not (f x)))
(declare curry ((Tuple :left :right -> :result) -> :left -> :right -> :result))
(define (curry func left right)
"Take a function whose input is a tuple and enable curried application of the left and right parameters, equivalent to `(func (Tuple left right))`."
(func (Tuple left right)))
(declare uncurry ((:left -> :right -> :result) -> Tuple :left :right -> :result))
(define (uncurry func tpl)
"Take a function with two currying parameters and enable their input as a single `Tuple`."
(match tpl
((Tuple left right)
(func left right))))
(declare pair-with ((:left -> :right) -> :left -> Tuple :left :right))
(define (pair-with func left)
"Create a `Tuple` of the form `(Tuple left (func left))`."
(Tuple left (func left)))
;;
;; Monadic operators
;;
(declare msum ((Monoid :a) (Foldable :t) => :t :a -> :a))
(define (msum xs)
"Fold over a list using `<>`."
(foldr <> mempty xs))
(declare asum ((Alternative :f) (Foldable :t) => :t (:f :a) -> :f :a))
(define (asum xs)
"Fold over a list using `alt`."
(foldr alt empty xs))
(declare /= (Eq :a => :a -> :a -> Boolean))
(define (/= a b)
"Is `a` not equal to `b`?"
(boolean-not (== a b)))
;;
;; Instances
;;
(define-instance (Functor (Arrow :a))
(define map compose))
(define-instance (Applicative (Arrow :a))
(define (pure x) (fn (_) x))
(define (liftA2 f g h) (fn (x) (f (g x) (h x)))))
(define-instance (Monad (Arrow :a))
(define (>>= f g) (fn (x) (g (f x) x)))))
;;;
;;; Bracket pattern
;;;
(cl:defmacro %unwind-protect (obj exit thunk)
"A wrapper on `cl:unwind-protect.`"
(cl:let ((output (cl:gensym "OUTPUT")))
`(cl:let (,output)
(cl:unwind-protect (cl:setq ,output (call-coalton-function ,thunk ,obj))
(call-coalton-function ,exit ,obj))
,output)))
(coalton-toplevel
(declare bracket (Monad :m
=> :m :a
-> (:a -> :m :b)
-> (:a -> :m :c)
-> :m :c))
(define (bracket init exit body)
"Bracket takes an initial state, performs a body of operations, and then forces a safe exit.
This wraps `cl:unwind-protect`.
Modeled after Haskell: https://wiki.haskell.org/Bracket_pattern"
(do
(obj <- init)
(lisp (:m :c) (obj exit body)
(%unwind-protect obj exit body)))))
#+sb-package-locks
(sb-ext:lock-package "COALTON-LIBRARY/FUNCTIONS")