generateMelodicSeed = slow 4
$ linger 0.5
$ repeatCycles 3
-- $ palindrome \n
$ (+ (slow (irand (4)+1) (sometimes (inversion) (run (irand (4)+1)))))
$ slow ((irand 3) + 1)
$ e ("x"<~>(irand 8)) 8
$ "x*16"<~>(irand 5)
let melody = slow 6 $ "0 2 [4 8 .] [3 4 3] 8 4 9"
d1
$ note
((scaleP scalePattern
$ off 4 ((+ 2 ).slow 2)
$ off 1 (inversion.slow 2)
$ off 3 (inversion.slow 3)
$ off 1.5 ((+ 2).rev.slow 2)
$ generateMelodicSeed
))#s "[pe-gtr:10,midi]" #gain 1 #orbit 0 #midichan 1
d4
-- $ rev \n
$ note
((scaleP scalePattern
-- $ off 4 ((+ 2 ).slow 2) \n
-- $ off 1 (inversion.slow 2) \n
$(rotR 1.5 )
$(+ slow 8 "x" <~> generateMelodicSeed)
-- $ inversion \n
$ generateMelodicSeed
))#s "[pe-gtr:12,midi]" #gain 1.2 #orbit 3 #midichan 4
Functional Core, Imperative Shell
:cc0:
Functional Core, Imperative Shell is a software architecture pattern that I was first exposed to by watching a talk by Destroy All Software. Systems organized according to this principle place as much business logic as possible inside of pure, referentially transparent functions and places all interaction with the outside world in imperative routines and/or object oriented classes. In theory, a system organized this way gains much of the benefits of a pure functional system without the mental/mathematical gymnastics required to write pure functional code whose purpose is to generate/organize side effects. (ex. Monads, monoids, endofunctors, ect...) As the system's internals are pure, they can be tested without mock objects or collaborators. The systems unit tests are then more accurately measuring the production behavior and are independent of internal implementation. The outer layer may be tested using mock versions of the functions contained within.