Для
Добавить б === Добавить б а
вам нужно поменять местами конструкторы AddL/AddR
следующим образом:
to :: Add a b -> Add b a
to (AddL x) = AddR x
to (AddR x) = AddL x
-- from = to
Для
Добавить пустоту a === a
вам нужна полиморфная функция void : Void -> a
to :: Add Void a -> a
to (AddL impossible) = void impossible
to (AddR x) = x
from :: a -> Add Void a
from x = AddR x
Переменная impossible
обозначает "несуществующее" значение типа Void
. Такого значения действительно нет (кроме дна/неопределенности). Вот почему линия
to (AddL impossible) = ...
на самом деле недостижимый код - он никогда не будет выполнен.
Функция void
использует тот факт, что для получения значения a
из воздуха требуется невозможный аргумент. К сожалению, в Haskell void
нельзя определить, если только не использовать неопределенность, например.
void :: Void -> a
void _ = error "This will never be reached"
Более элегантным и правильным решением было бы
void :: Void -> a
void x = case x of
-- no constructors for Void, hence no branches here to do!
-- since all these (zero) branches have type `a`, the whole
-- case has type `a` (!!!)
но, увы, Haskell запрещает пустые конструкции case
. (В GHC 7.8 пустые регистры разрешены через EmptyCase, как указывает bheklilr).
Для сравнения, в языке с зависимой типизацией, таком как Coq или agda, приведенный выше код (с небольшими изменениями) был бы в порядке. Вот это в Coq:
Inductive Void : Set := . (* No constructors for Void *)
Definition void (A : Set) (x : Void) : A :=
match x with
end .
А в Агде
data Void : Set where
-- no constructors
void : (A : Set) -> Void -> A
void A ()
-- () is an "impossible" pattern in Agda, telling the compiler that this argument
-- has no values in its type, so one can omit the definition entirely.
person
chi
schedule
01.08.2014