DimaSamoz / mezzo

A Haskell library for typesafe music composition
MIT License
357 stars 12 forks source link

Can't compile progressions #4

Closed sergei-mironov closed 7 years ago

sergei-mironov commented 7 years ago

Hi! Thanks for the nice package. I tried to compile examples from the README, but got stuck near the progressions section. Could you please describe how to compile progressions into midi?

My attempt looks as follows

p = defScore $ play $ prog $ ph_IVI ton dom_V ton :+ cadence (full subdom_IV auth_V7_I)

main :: IO ()
main = do
  renderScore "comp.mid" "-" p
  system "timidity comp.mid"
  return ()

In response, I got a screen full of letters...

src/M1.hs:23:23: error:
    • Couldn't match expected type ‘Melody
                                      'Mezzo.Model.Music.Sig m0 d0’
                  with actual type ‘Music
                                      'Mezzo.Model.Music.Sig
                                      (Mezzo.Model.Harmony.Functional.FromProg
                                         ('Mezzo.Model.Harmony.Functional.PhraseIVI
                                            ('Mezzo.Model.Harmony.Functional.TonT
                                               'Mezzo.Model.Harmony.Functional.DegChord)
                                            ('Mezzo.Model.Harmony.Functional.DomVM
                                               'Mezzo.Model.Harmony.Functional.DegChord)
                                            ('Mezzo.Model.Harmony.Functional.TonT
                                               'Mezzo.Model.Harmony.Functional.DegChord)
                                          'Mezzo.Model.Harmony.Functional.:= 'Mezzo.Model.Harmony.Functional.CadPhrase
                                                                               ('Mezzo.Model.Harmony.Functional.FullCad
                                                                                  ('Mezzo.Model.Harmony.Functional.SubIV
                                                                                     'Mezzo.Model.Harmony.Functional.DegChord)
                                                                                  ('Mezzo.Model.Harmony.Functional.AuthCad7
                                                                                     'Mezzo.Model.Harmony.Functional.DegChord
                                                                                     'Mezzo.Model.Harmony.Functional.DegChord)))
                                         t10)’
    • In the second argument of ‘($)’, namely
        ‘prog $ ph_IVI ton dom_V ton :+ cadence (full subdom_IV auth_V7_I)’
      In the second argument of ‘($)’, namely
        ‘play
         $ prog
           $ ph_IVI ton dom_V ton :+ cadence (full subdom_IV auth_V7_I)’
      In the expression:
        defScore
        $ play
          $ prog $ ph_IVI ton dom_V ton :+ cadence (full subdom_IV auth_V7_I)
Failed, modules loaded: none.

As a more in-depth feedback, let me state that idea is of music-dsl with rule-checking is truly brilliant! I would be happy to use such DSL to learn music theory. Unfortunately, in current implementation, GHC error messages are nearly impossible to understand! I would suggest to re-implement the language as full-scale DSL (e.g. using QuasyQuotes) with its own compiler/interpreter. I believe such a language may live with some well-known typechecker like HindleyMilner. Overcomplicated GHC types kill all the fun!

DimaSamoz commented 7 years ago

Hi! Thanks for your interest :) I think the issue in your case is the play function: it is only intended to convert melodies into Music values, which is what prog does with progressions. In very simplistic terms, you have:

Hence you only need

p = defScore $ prog $ ph_IVI ton dom_V ton :+ cadence (full subdom_IV auth_V7_I)

As an aside, play was renamed to start in the latest release (which I just added) to avoid confusion with the live playback functions.

Once you have a Score (by applying defScore or the more general score construction functions to a Music value), you can use renderScore to render it into a MIDI file (that part of your program seems correct).

Thanks for the kind words! I do agree that type-level hackery is a bit risky – it's quite easy to break stuff and output an enormous and scary error message. However, it's quite nice when it works! I am considering rewriting Mezzo on the term level (or even TH, as you suggest) in the future to compare the benefits and drawbacks of the two approaches (though this will have to wait as I need to concentrate on a different project this year).

I hope this helped!