2016-02-04 8 views
0

TL; DRГетерогенный Список для DSL

является их способ, которым я могу построить гетерогенную список ограниченного в какой-то класс типов, без вызова конструктора по каждому элементу?


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

Нужный синтаксис для моего DSL-то вроде

from :: String -> Query 
select :: [String] -> Query -> Query 
select ["this", "that"] $ from "tbl" 

беда в том, что я хотел бы, чтобы такие вещи, как столбец арифметических и логических операторов в аргументе для выбора. Например

(<~) :: String -> Column -> Column -- used for assigning a new name 
add :: String -> String -> Column 

select ["stuff" <~ "this" `add` "that", "this"] $ from "tbl" 

выход которого может быть что-то вроде

SELECT 
    this + that as stuff, 
    this 
FROM tbl; 

Очевидная проблема в том, что это требует гетерогенный список. Я мог бы создать новый тип данных, чтобы обернуть эти значения и идти дальше с моей жизнью, но я думаю, что результат является гораздо более громоздким

select ["stuff" <~ (Column "this") `add` (Column "that"), Column "this", Column "that"] $ from "tbl" 

Я видел некоторые трюки с помощью GADT-х и экзистенциалов, но все они требуют намотав каждый значение в конструкторе, в то время как я упрямо хочу, чтобы мои строки были такими, какие есть. Является ли это возможным??

+0

Не так, но вы можете использовать Template Haskell для создания некоторого пользовательского синтаксиса. Предупреждение: не для слабонервных. Кроме того, есть несколько трюков, которые вы можете играть с классами, чтобы упростить запись гетерогенных списков (пакет 'HList' имеет такую ​​вещь). Это будет выглядеть красиво, но ваши сообщения об ошибках будут страшными. – dfeuer

ответ

2

GHC имеет расширение для случаев так же, как этот: OverloadedStrings. Основная идея OverloadedStrings заключается в том, что строковые литералы могут быть любыми типами, которые реализуют класс Data.String.IsString. Поэтому для вашего кода вы можете сделать что-то подобное.

import Data.String 

newtype Column = Column String 

instance IsString Column where 
    fromString = Column 

А затем определите свои комбинаторы так, как вы были. Теперь нулевые струнные литералы будут интерпретироваться как Columns.

Main*> :t ["those" <~ "this" `and` "that", "thing"] 
["those" <~ "this" `and` "that", "thing"] :: [Column] 
+0

Кажется, это именно то, что мне нужно –

0

Нет, в Haskell невозможны соответствующие гетерогенные списки. Вам всегда понадобится какой-то конструктор. Однако может быть другое решение вашей проблемы. Вы можете переопределить свои комбинаторы, чтобы вернуть String s вместо Column s. Возможно, что-то вроде этого.

infixl 5 `add` 
add :: String -> String -> String 
add a b = a ++ " + " ++ b 

infix 2 <~ 
(<~) :: String -> String -> String 
new <~ old = old ++ " as " ++ new 
+0

Я хочу в какой-то момент включить тип, гарантирующий, что запрос не будет генерировать что-то странное. Например, я не хочу разрешать такие операции, как '' 'select [" this ", sum" that "] $ from" tbl "' '', поскольку это выбор одного значения и вектора, в то время как я бы разрешил '' 'select ["this" 'add' (sum" that ")] $ from" tbl "' '' –