2014-02-08 3 views
2

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

data Foobar x = 
    Foo1 {field1 :: x, field2 :: String} | 
    Foo2 {field1 :: x, field3 :: Int} | 
    Foo3 {    field4 :: Bool} | 
    Foo4 {    field2 :: String, field4 :: Bool} 

Как вы можете видеть, некоторые конструкторы зависят от x, но другие этого не делают. Я пытаюсь написать функцию, аналогичную fmap:

transform :: (x -> y) -> Foobar x -> Foobar y 
transform fn foobar = 
    case foobar of 
    Foo1 {} -> foobar {field1 = fn (field1 foobar)} 
    Foo2 {} -> foobar {field1 = fn (field1 foobar)} 
    _  -> foobar 

Как вы можете видеть, запись синтаксис аккуратно позволяет мне избежать необходимости перестраивать весь конструктор, применяя fn только там, где это необходимо. К сожалению, это происходит, когда fn необходим в ноль мест. В этом случае (т. Е. Конечная альтернатива) выражение не проверяет тип. Мне совершенно ясно почему это терпит неудачу - но я озадачен как исправить это.

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

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

+0

Можете ли вы просто добавить 'получение Functor' и позволить компилятору написать для вас все 25 случаев? –

+0

@ DanielWagner Реальный тип имеет несколько параметров типа - и я намерен написать одну функцию 'transform' для каждого параметра. Я не знаю, как это получить. – MathematicalOrchid

+0

[Этот вопрос] (http://stackoverflow.com/q/4069840/485115) может вас заинтересовать. –

ответ

4

One решение (для сохранения набора текста) заключается в использовании учетных карточек:

{-# LANGUAGE RecordWildCards #-} 
-- ... 
case foobar of 
    Foo4 {..} -> Foo4 {..} 
+0

Ооо, это интересно ... Кажется, прагма также включает некоторые другие вещи, которые я не хочу, но если я помещу это только в модуль, который определяет функции преобразования, он должен быть безопасным ... – MathematicalOrchid

2

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

{-# LANGUAGE DeriveFunctor #-} 
    data Foo a = ... 
    deriving(Functor) 

Теперь мы можем написать более безопасную форму unsafeCoerce

coerceFoo :: Foo a -> Foo b 
    coerceFoo = fmap (error "This shouldn't be used on a phantom type") 

И использовать это в вашем примере

_  -> coerceFoo foobar 
+0

Использование 'newtype' для перевода порядка параметров ... Какая гениальная идея. Интересно, работает ли это на самом деле? Я попробую ... – MathematicalOrchid

+0

@MathematicsOrchid Drat это не так, поскольку у нас нет способа преобразовать 'b' в Foo .. неважно – jozefg

+0

Aww. :-(Ну ладно ... – MathematicalOrchid

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