2014-08-31 1 views
4

Недавно я сдал экзамен, в основном из-за вопроса EDSL. Я не понял этих концепций, поэтому я думаю, почему я потерпел неудачу. Я думаю, что объяснение моих учителей было абстрактным для меня, поэтому я задаюсь вопросом, может ли кто-нибудь объяснить это более четко.Примитивные производные операции и комбинаторы в EDSL: s

Интересно, может ли кто-нибудь кратко объяснить, что такое компоненты EDSL, и что их характеризует. В нашем курсе мы прошли через мелкое и глубокое Погружение в DSL и посмотрели на следующие строительные блоках для DSL:

  1. Конструкторов
  2. Комбинаторов (примитивный и производных)
  3. Run функции

Я думаю, что функции конструктора и запуска более понятны, поэтому мне больше интересно понять, что делает комбинатор производным или примитивным. Это не больно, если кто-то объяснит другие понятия. Вот пример из наших лекций для справки. Ее неглубокий из реализации внешних DSL для создания сигналов:

module Signal.Shallow 
(Time 
-- | the 'Signal' type is abstract 
, Signal 
-- * Smart constructors 
, constS, timeS 
-- * Combinators 
, ($$), mapT 
-- * Derived operation 
, mapS 
-- * Run function 
, sample 
) where 

-- * Smart constructors 
constS :: a -> Signal a 
timeS ::  Signal Time 
-- * Combinators 
($$) :: Signal (a -> b) -> Signal a -> Signal b 
mapT :: (Time -> Time) -> Signal a -> Signal a 
-- * Derived operation 
mapS :: (a -> b)  -> Signal a -> Signal b 
-- * Run function 
sample :: Signal a -> Time -> a 

type Time = Double 
newtype Signal a = Sig {unSig :: Time -> a} 

-- | The constant signal. 
constS x = Sig (const x) 

-- | The time signal 
timeS = Sig id 

-- | Function application lifted to signals. 
fs $$ xs = Sig (\t -> unSig fs t (unSig xs t)) 

ответ

4

Примитивный комбинатор является тот, который встроен в DSL, определенный в базовом языке (т.е. Haskell). DSL часто построены вокруг абстрактного типа - тип, реализация которого скрыта для конечного пользователя. Это абсолютно непрозрачно. Первобытные комбинаторы, представленные языком, - это те, которые должны знать, как абстракция фактически реализуется для работы.

Производный комбинатор, с другой стороны, может быть реализован в терминах других комбинаторов уже в DSL. Это не нужно знать что-либо об абстрактных типах. Другими словами, производный комбинатор один Вы бы написали.

Это очень похоже на идею примитивных типов в самом Haskell. Например, вы не можете реализовать Int или Int, например, +. Они требуют, чтобы вещи, встроенные в компилятор, работали, потому что номера обрабатывались специально. С другой стороны, Bool является не примитивный; вы можете написать его как библиотеку.

data Bool = True | False -- ... you can't do this for Int! 

«Примитив» и «производным» для DSL, это та же идея, за исключением того, компилятор на самом деле ваша библиотека Haskell.

В вашем примере Signal - это абстрактный тип. Он реализован как функция Time -> a, но эта информация не экспортируется из модуля. В будущем вы (как автор DSL) можете свободно изменять способ выполнения Signal. (И в самом деле, вы действительно хотите: это не является эффективным представление и использование Double для времени привередливо.)

функции как $$ примитивно, потому что это зависит от того, зная, что Signal является Time -> a. Когда вы измените представление Signal, вам придется переписать $$. Кроме того, пользователь вашей библиотеки не сможет самостоятельно реализовать $$.

С другой стороны, mapS является производной операцией, поскольку он может быть написан полностью с точки зрения других вещей, которые вы экспортируете. Он не должен знать ничего особенного о Signal и может даже быть написан одним из пользователей библиотеки. Реализация может выглядеть примерно так:

mapS f signal = constS f $$ signal 

Обратите внимание, как он использует constS и $$, но никогда не разворачивает signal. Знание как для разворачивания сигнала полностью скрывается в этих двух функциях. mapS является «производным», потому что он написан только в вашем DSL, не нуждаясь ни в чем ниже вашего уровня абстракции. Когда вы измените реализацию Signal, mapS по-прежнему будет работать как есть: вам просто нужно обновить constS и $$, и вы получите mapS бесплатно.

Итак: примитивные комбинаторы - это те, которые встроены непосредственно на ваш язык и должны знать о его внутренних деталях реализации. Производные комбинаторы записываются чисто в терминах вашего языка и не зависят от данных внутренних деталей. Это просто удобные функции, которые так же легко могут быть написаны конечным пользователем вашей библиотеки.

+1

Отличное объяснение. Благодаря! – patriques