Is there, in Haskell, something similar to sub-guards?

前端 未结 3 1715
离开以前
离开以前 2020-12-03 17:34

I\'m writing a program on the classification of musical intervals. The conceptual structure is quite complicated and I would represent it as clearly as possible. The first f

3条回答
  •  鱼传尺愫
    2020-12-03 18:18

    This isn't really an answer to the title question, but adresses your particular application. Similar approaches will work for many other problems where you might wish for such sub-guards.

    First I'd recommend you start out less “stringly typed”:

    interval' :: PitchSpec -> PitchSpec -> Interval
    
    data Interval = Unison PureQuality
                  | Second IntvQuality
                  | Third IntvQuality
                  | Fourth PureQuality
                  | ...
    
    data IntvQuality = Major | Minor | OtherQual IntvDistortion
    type PureQuality = Maybe IntvDistortion
    data IntvDistortion = Augm Int | Dimin Int   -- should actually be Nat rather than Int
    

    And regardless of that, your particular task can be done much more elegantly by “computing” the values, rather than comparing with a bunch of hard-coded cases. Basically, what you need is this:

    type RDegDiatonic = Int
    type RDeg12edo = Rational  -- we need quarter-tones for neutral thirds etc., which aren't in 12-edo tuning
    
    courseInterval :: RDegDiatonic -> (Interval, RDeg12edo)
    courseInterval 0 = ( Unison undefined, 0   )
    courseInterval 1 = ( Second undefined, 1.5 )
    courseInterval 2 = ( Third undefined,  3.5 )
    courseInterval 3 = ( Fourth undefined, 5   )
    ...
    

    You can then “fill in” those undefined interval qualities by comparing the 12edo-size with the one you've given, using1

    class IntervalQuality q where
      qualityFrom12edoDiff :: RDeg12edo -> q
    
    instance IntervalQuality PureQuality where
      qualityFrom12edoDiff n = case round n of
             0 -> Nothing
             n' | n'>0       -> Augm n
                | otherwise  -> Dimin n'
    instance IntervalQuality IntvQuality where
      qualityFrom12edoDiff n | n > 1      = OtherQual . Augm $ floor n
                             | n < -1     = OtherQual . Dimin $ ceil n
                             | n > 0      = Major
                             | otherwise  = Minor
    

    With that, you can implement your function thus:

    interval pt1 pt2 = case gd of
           0 -> Unison . qualityFrom12edoDiff $ sd - 0
           1 -> Second . qualityFrom12edoDiff $ sd - 1.5
           2 -> Third  . qualityFrom12edoDiff $ sd - 3.5
           3 -> Fourth . qualityFrom12edoDiff $ sd - 5
           ...
    


    1You don't really need a type class here, I could as well have defined two diffently-named functions for pure and other intervals.

提交回复
热议问题