Skip to content

Instantly share code, notes, and snippets.

@newlandsvalley
Created August 12, 2016 13:55
Show Gist options
  • Save newlandsvalley/733ced7c86b738732028b4480e83980e to your computer and use it in GitHub Desktop.
Save newlandsvalley/733ced7c86b738732028b4480e83980e to your computer and use it in GitHub Desktop.
elm-check Producers and recursive data structures
module Checks exposing (..)
import Producers exposing (..)
import Mus exposing (..)
import Check exposing (..)
import Check.Producer exposing (..)
import Check.Test
import ElmTest
-- Tests for Music
claim_retro_retains_duration =
claim
"a line of music reversed retains its duration"
`that`
(\m -> duration (retro m))
`is`
(\m -> duration m)
`for`
music
suite_music =
suite "Music Suite"
[
claim_retro_retains_duration
]
evidence : Evidence
evidence = quickCheck suite_music
main =
ElmTest.runSuite (Check.Test.evidenceToTest evidence)
module Mus exposing (..)
{-|
@docs Music, duration, retro
-}
import Basics exposing (max)
{-| a reduced form of HSoM's Music data type -}
type Music =
Note Int
| Rest Int
| Seq Music Music
| Par Music Music
{-| a reduced form of a function to get the duration of a piece of music -}
duration : Music -> Int
duration m =
case m of
Note i ->
i
Rest i ->
i
Seq a b ->
(duration a) + (duration b)
Par a b ->
max (duration a) (duration b)
{-| a retrogade piece -}
retro : Music -> Music
retro m =
case m of
Note _ ->
m
Rest _ ->
m
Seq m1 m2 ->
Seq (retro m2) (retro m1)
Par m1 m2 ->
let d1 = duration m1
d2 = duration m2
in
if (d1 == d2) then
Par (retro m2) (retro m1)
else if (d1 > d2) then
Par (retro m1) (Seq (Rest (d1 - d2)) (retro m2))
else
Par (Seq (Rest (d2 - d1)) (retro m1)) (retro m2)
module Producers exposing (..)
import Random exposing (Generator)
import Check.Producer exposing (..)
import Random.Extra exposing (..)
import Mus exposing (..)
import Debug exposing (..)
{-| a producer for a single note -}
note : Producer Music
note =
rangeInt 1 8
|> Check.Producer.map Note
{-| a producer for a single rest -}
rest : Producer Music
rest =
rangeInt 1 8
|> Check.Producer.map Rest
{-| a sequential or parallel production of music -}
parseq : (Music -> Music -> Music) -> Producer Music
parseq constructor =
let
{- replace the line below with
pair = Check.Producer.tuple (note, note)
and the runtime doesn't crash
-}
pair = Check.Producer.tuple (music, music)
_ = log "parseq" pair
in
pair
|> Check.Producer.map (\(a,b) -> constructor a b)
{-| music -}
music : Producer Music
music =
let
generator =
choices
[ note.generator
, rest.generator
, (parseq Seq).generator
, (parseq Par).generator
]
shrinker pr =
case pr of
Note _ -> note.shrinker pr
Rest _ -> rest.shrinker pr
Seq _ _ -> (parseq Seq).shrinker pr
Par _ _ -> (parseq Par).shrinker pr
in
Producer generator shrinker
@newlandsvalley
Copy link
Author

I'm trying to learn how to write an elm-check Producer for a recursive data structure. The Music data type refers to itself in its constructors. The parseq function in Producers.elm (as written) is intended to build Music trees of arbitrary depth. However, the check crashes at run time with the error

TypeError: _user$project$Producers$parseq is not a function

If I prevent recursive generation by replacing the line

pair = Check.Producer.tuple (music, music) 

with the non-recursive

pair = Check.Producer.tuple (note, note) 

then the check runs (albeit with limited data)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment