[haskell] “major C”대신 “C major”를 쓸 기회가 있습니까?

음악 프로젝트에서 약간의 미적 문제가 발생하여 한동안 버그가 발생했습니다.

나는 타입을 가지고 있으며 a 와 a를 data Key = C | D | ...구성 할 수 있습니다 . 주요 및 사소한 규모를 예 구분합니다.ScaleKeyModeMode

나는 정의 할 수 있습니다 Mode에서 함수 등의 형태 Key로를 Scale. 이 경우 모드는 소문자 이름을 가지며 (괜찮음) 다음과 같이 Scale을 얻을 수 있습니다

aScale = major C

그러나 음악가들은 이렇게 말하지 않습니다. 그들은이 척도를 주요 C 척도가 아닌 C 주요 척도로 지칭합니다 .

내가 원하는 것

이상적으로 쓰고 싶습니다

aScale = C major

이것이 가능합니까?

내가 시도한 것

from Key을 구성하는 함수를 만들 수 있으므로 쓸 수 있습니다.ScaleMode

aScale = c Major

그러나 키를 스케일 생성에 국한시킬 수는 없습니다. 다른 것들도 필요합니다 (예 : 화음 구성 ). 또한 Key의 인스턴스 여야합니다 Show.


추가 함수 (또는 값 생성자)를 사용할 때 Mode이후 를 넣을 수 있습니다 Key.

aScale = scale C majorscale :: Key -> Mode -> Scale

그러나 여분의 단어 스케일 은 시끄럽고 그 이름과 반대로 보이지만 scale실제로 스케일과 관련이 없습니다. 지능형 부분에 major, scale정말입니다 flip ($).


A는 사용 newtype Mode = Major | Minor ...을 제외하고 정말 많이 변경되지 않습니다 scale요구하는 것은 더 지능이 될 수 있습니다 :

aScale = scale C Major



답변

해결책 1 :

이것을 사용하십시오

data Mode  = Major | Minor
data Scale = C Mode | D Mode | E Mode | F Mode | G Mode | A Mode | B Mode 

이제 대문자 C와 대문자 M을 쓸 수 있습니다.

aScale = C Major

해결책 2a :

이것도 가능합니다

data Mode  = Major | Minor
data Key   = C | D | E | F | G | A | B

data Scale = Scale Key Mode  

이제 당신은 작성

aScale = Scale C Major

해결책 2b :

이것도 가능합니다

data Mode  = Major | Minor
data Key   = C | D | E | F | G | A | B

type Scale = (Key, Mode)  

이제 당신은 작성

aScale = (C, Major)


답변

다음은 실제로 권장하지 않지만 매우 “음악적”인 기발한 솔루션입니다.

infix 8 
(♮) :: Key -> Mode -> Scale
(♮) = (Data.Function.&)
 -- ≡ flip ($)

그럼 당신은 쓸 수 있습니다

> C major :: Scale

이 정말 목표로 물론, 당신도 가질 것입니다 F♯ minorB♭ major


답변

추가 연산자가 마음에 들지 않으면 &에서 사용할 수 있습니다 Data.Function. 그것이 major함수 라고 가정하면 Key -> Scale쓸 수 C & major있습니다. 그 결과 Scale가치가 있습니다.

Prelude Data.Function> :t C & major
C & major :: Scale


답변

이미 몇 가지 좋은 대답이 있지만 여기에 도움이 될 수있는 연속 전달 스타일 솔루션이 있습니다 (이 특정 예에서는 아니지만 일종의 역 응용 구문이 필요한 다른 상황에서).

일부 문제 도메인 유형에 대한 표준 정의 :

data Mode = Major | Minor                 deriving (Show)
data Key = C | D | E | F | G | A | B      deriving (Show)
data Semitone = Flat | Natural | Sharp    deriving (Show)

data Note = Note Key Semitone             deriving (Show)
data Scale = Scale Note Mode              deriving (Show)
data Chord = Chord [Note]                 deriving (Show)

연속 전달 유형을 도입 할 수 있습니다.

type Cont a r = (a -> r) -> r

기본 노트 작성 유형을 작성하여 Cont다음과 같이 유형을 작성하십시오 .

a, b, c :: Cont Note r
a = mkNote A
b = mkNote B
c = mkNote C
-- etc.
mkNote a f = f $ Note a Natural

flat, natural, sharp :: Note -> Cont Note r
flat    = mkSemi Flat
natural = mkSemi Natural
sharp   = mkSemi Sharp
mkSemi semi (Note k _) f = f $ Note k semi

그런 다음, 스케일, 음표 및 코드 작성 기능은 Conts를 접두사 형식 (예 Cont: 으로 전달되는 연속)으로 일반 유형으로 해석 할 수 있습니다 .

major, minor :: Note -> Scale
major n = Scale n Major
minor n = Scale n Minor

note :: Note -> Note
note = id

또는 접두사 형식 (예 Cont: s를 인수로 사용) :

chord :: [Cont Note [Note]] -> Chord
chord = Chord . foldr step []
  where step f acc = f (:acc)

이제 다음과 같이 쓸 수 있습니다 :

> c sharp note
Note C Sharp
> c note
Note C Natural
> c major
Scale (Note C Natural) Major
> b flat note
Note B Flat
> c sharp major
Scale (Note C Sharp) Major
> chord [a sharp, c]
Chord [Note A Sharp,Note C Natural]

참고 c자체가없는 Show인스턴스를, 그러나 c note않습니다.

Note유형을 수정하면 이중 실수 (예 : c sharp sharp와 구별됨 d) 등을 쉽게 지원할 수 있습니다 .


답변

그러나 키를 스케일 구성에 국한시킬 수는 없습니다. 다른 것들도 필요합니다 (예 : 화음 구성). 또한 Key는 Show의 인스턴스 여야합니다.

타입 클래스를 사용하여 그 문제를 영리하게 해결할 수 있습니다.

{-# LANGUAGE FlexibleInstances #-}

data Key = C | D | E | F | G | A | B deriving(Show)

data Mode = Major | Minor

data Scale = Scale Key Mode

class UsesKey t where
  c, d, e, f, g, a, b :: t

instance UsesKey Key where
  c = C
  d = D
  e = E
  f = F
  g = G
  a = A
  b = B

instance UsesKey (Mode -> Scale) where
  c = Scale C
  d = Scale D
  e = Scale E
  f = Scale F
  g = Scale G
  a = Scale A
  b = Scale B

aScale :: Scale
aScale = c Major

이제 적절한 인스턴스를 정의하여 다른 유형에도 소문자를 사용할 수 있습니다.


답변