if cond (then) (else) = [[ default-take [take :truthy cond] (:false else) then ]] true = (:truthy :true) false = (:truthy :false) equal? a b = [default-take a (b true) false] not v = [if v [give :truthy :false v] [give :truthy :true v]] empty? map = [default-take map (() true) false] zero = () succ num = [give :pred num ()] pred num = [take :pred num] add a b = [ if [empty? a] b [succ [add [pred a] b]] ] two = [succ [succ zero]] identity a = a const a = \_ -> a . f g = \x -> [f [g x]] nil = () cons value list = (:head value :tail list) apply fn v = [fn v] assert c = [if c :pass :fail] map f xs = [ if [empty? xs] nil [cons [f [take :head xs]] [map f [take :tail xs]]] ] filter p? xs = [ if [empty? xs] nil let first = [take :head xs] rest = [take :tail xs] in [if [p? first] [cons first [filter p? rest]] [filter p? rest]] ] silly-list = [cons :a [cons :b nil]] silly-with-empties-list = [cons () [cons () [cons :a [cons () [cons :b [cons () nil]]]]]] silly-fn v = [take v (:a :c :b :d)] expected-map-list = [cons :c [cons :d nil]] expected-filter-list = [cons :c [cons :d nil]] always-true _ = true cases = (| (:name :equal-for-keywords :assertion [equal? :foo :foo]) (:name :equal-for-equal-maps :assertion [equal? two two]) (:name :equal-for-non-equal-maps :assertion [not [equal? zero two]]) (:name :equal-for-functions :assertion [equal? cons cons]) (:name :cons-works :assertion [equal? [cons :foo nil] (:head :foo :tail ())]) (:name :add-works-for-trivial-case :assertion [equal? [add zero two] two]) (:name :add-works-for-harder-case :assertion [equal? [add two two] [succ [succ two]]]) (:name :first-class-functions :assertion [equal? [apply identity :foo] :foo]) (:name :map-for-trivial-case :assertion [equal? [map identity silly-list] silly-list]) (:name :map-for-harder-case :assertion [equal? [map silly-fn silly-list] expected-map-list]) (:name :filter-for-trivial-case :assertion [equal? [filter always-true silly-list] silly-list]) (:name :filter-for-harder-case :assertion [equal? [filter [. not empty?] silly-with-empties-list] silly-list]) (:name :compose-for-trivial-case :assertion [[. identity identity] true]) (:name :compose-for-harder-case :assertion [[. not not] true]) |) taking key = \map -> [take key map] assertion-fails case = [ not [take :assertion case] ] run-tests cases = [ map [taking :name] [filter assertion-fails cases] ] main = [run-tests cases]