52700.fb2
type Dur a :: *
dur
:: a -> Dur a
delay
:: Dur a -> a -> a
stretch :: Dur a -> a -> a
В этом классе определён один тип, который обозначает размерность времени, и три метода в дополнении
к методам delay и stretch мы добавим метод dur, мы будем считать, что всё что происходит во времени
конечно и с помощью метода dur мы всегда можем узнать протяжённость значения их класса Temporal во
времени. Для определения этого класса нам придётся подключить расширение TypeFamilies. Теперь мы
легко можем определить экземпляры класса Temporal для Event и Track:
instance Num t => Temporal (Event t a) where
type Dur (Event t a) = t
dur
= eventDur
delay
= delayEvent
stretch = stretchEvent
instance Num t => Temporal (Track t a) where
type Dur (Track t a) = t
dur
= trackDur
delay
= delayTrack
stretch = stretchTrack
Композиция треков
Определим две полезные в музыке операции: параллельную и последовательную композицию треков. В
параллельной композиции мы играем два трека одновременно:
(=:=) :: Ord t => Track t a -> Track t a -> Track t a
Track t es =:= Track t’ es’ = Track (max t t’) (es ++ es’)
Теперь общая длительность трека равна длительности большего из треков, а события включают в себя
события каждого из треков. С помощью преобразований во времени мы можем определить последовательную
композицию, для этого мы сместим второй трек на длину первого и сыграем их одновременно:
308 | Глава 21: Музыкальный пример
(+:+) :: (Ord t, Num t) => Track t a -> Track t a -> Track t a
(+:+) a b = a =:= delay (dur a) b
При этом у нас как раз и получится, что мы сначала сыграем целиком трек a, а затем трек b. Теперь
определим аналоги операций =:= и +:+ для списков:
chord :: (Num t, Ord t) => [Track t a] -> Track t a
chord = foldr (=:=) (silence 0)
line :: (Num t, Ord t) => [Track t a] -> Track t a
line = foldr (+:+) (silence 0)
Мы можем определить в терминах этих операций цикличный повтор событий:
loop :: (Num t, Ord t) => Int -> Track t a -> Track t a
loop n t = line $ replicate n t
Экземпляры стандартных классов
Мы можем сделать тип трек экземпляром класса Functor:
instance Functor (Event t) where
fmap f e = e{ eventContent = f (eventContent e) }