2014-09-04 3 views
0

меня тип данных пользовательского схематически определены следующим образом:Приложить типа к функции

data Foo = Foo 
    { f1 :: Int 
    , f2 :: b 
    , f3 :: c 
    } 

Что мне нужно сделать

Закрепить строку каждой функции, записанной в Foo. Это было довольно ясно в моей голове, что это было тривиально из-за полиморфизма haskell.


Сначала попробуйте: классы

class Value a where 
    val :: a -> String 
class Query e where 
    queries :: (Value a) => [(e -> a, String)] 

instance Value Int where val = show 
    instance Query Foo where 
    queries = 
     [ (f1, "function one") 
     , (other records of Foo…) 
     ] 

Который не работает. Я не уверен, что понял, почему. Но я думаю, что ghc ожидает функцию типа (Foo -> a), но получает функцию типа (Foo -> Int). Итак, здесь не применяется полиморфизм.


Вторая попытка: шаблон соответствия

keyOf :: (Foo -> a) -> String 
keyOf f1 = "function one" 
keyOf f2 = "function two" 
keyOf f3 = "function three" 
keyOf _ = "unknown function" 

я был вполне удовлетворен, видя, что компилируется. Тогда, в GHCI:

λ keyOf f2 = "function one" 

Может не шаблон матч против имени функции, очевидно ...


EDIT: Почему я должен сделать это

Построить строку запроса, как это:

"(keyOf f1)=(f1 Foo), (keyOf f2)=(f2 Foo), (keyOf f3)=(f3 Foo)" 

В более общем плане, свернуть каждую функцию r ecorded в Foo с связанной с ним строкой, и это результат. Пример:

exampleFoo :: Foo 
exampleFoo = Foo "one" "two" "three" 

assocs = [(f1, "function one"), (f2, "function two"), (f3, "function three")] 

result == "function one=one, function two=two, function three=three" 

Теперь я действительно интересно, если что-то трюк выполнимо без привлечения мета-программирования (например, TemplateHaskell) в Haskell. Любые варианты, которые я не рассматривал? Спасибо.

+0

Можете ли вы привести пример программы, которую вы хотите написать, используя строки, прикрепленные к каждой функции? –

+0

Да, я редактировал вопрос. Это достаточно ясно? – qleguennec

+3

Боюсь, что нет - я не могу понять, что вы имеете в виду в строке запроса. Может быть яснее с некоторым примером кода или псевдокодом. –

ответ

0

Я думаю, что основная проблема заключается в том, что вы сбиваете с толку, как работает data.

Глядя на

keyOf :: (Foo -> a) -> String 
keyOf f1 = "function one" 
keyOf f2 = "function two" 
keyOf f3 = "function three" 
keyOf _ = "unknown function" 

Я думаю, что, может быть, ваши данные должны выглядеть следующим образом:

data Foo b c = F1 Int | F2 b | F3 c 

Таким образом, матч шаблон будет:

keyOf :: Foo b c -> String 
keyOf (F1 _) = "function one" 
keyOf (F2 _) = "function two" 
keyOf (F3 _) = "function three" 

"unknown function" не нуждается, поскольку мы являемся сопоставлением с образцом каждый возможный конструктор Foo.

+1

Тип данных Foo состоит из 13 записей, и мне нужно, чтобы каждый из них построил мою последнюю строку запроса, поэтому тип суммы здесь не имеет значения. – qleguennec

+1

В этом случае сопоставление шаблонов может быть не тем, что вам нужно. – chamini2

+0

совсем нет, я понял это! – qleguennec

2

Я думаю, что вы ищете здесь экзистенциальный.

Если определить

data Selector e where 
    Selector :: Value a => (e -> a) -> Selector e 

(вам нужно {-# LANGUAGE GADTs #-} в верхней части файла)

Затем вы можете определить класс Query как:

class Query e where 
    queries :: [(Selector e, String)] 

instance Query Foo where 
    queries = 
     [ (Selector f1, "function one") 
     , (other records of Foo…) 
     ] 

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

В этом конкретном примере, единственное, что вы можете сделать с a типа в Selector является превратить его в String с Value, так что вы можете точно так же написали:

class Query e where 
    queries :: [(e -> String, String)] 

instance Query Foo where 
    queries = 
     [ (value . f1, "function one") 
     , (other records of Foo…) 
     ] 

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

+4

Это может быть правильный ответ на вопросы, но, пожалуйста, не рекомендуйте экзистенции, не сообщая о своих [спорах] (http://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/) (На самом деле я склонен продвигать использование экзистенциальных ситуаций в хороших вариантах использования, но я скорее сомневаюсь, что это одно). – leftaroundabout

+0

Это хорошая идея, но '[(Selector e, String)]' создаст список '[(Selector Foo, String)]' в примере Query Foo. Тогда, 'Selector f1 :: Selector Int' не будет применяться в этом контексте. – qleguennec

+2

Опечатка должна быть «Selector :: Value a => (e -> a) -> Selector e'. Но вам, вероятно, здесь не нужна экзистенция. Единственное, что вы можете сделать со значением типа 'Selector e', - это применить содержащуюся функцию к значению типа' e', но поскольку тип 'a' экзистенциально квантуется, единственное, что вы можете сделать с полученной вещью это вызов 'val' на нем, поскольку это единственное, что вы знаете о' a': это экземпляр 'Value'. Поскольку 'val' просто создает строку, вы можете просто иметь' data Selector e = Selector (e -> String) String' и 'queries :: [Selector]'. – user2407038

Смежные вопросы