52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 342

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 342

class Temporal a where

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) }