WTF?

Jan 20, 2011 17:57

Просто пейст небольшого куска кода на Хаскеле.

Введение для незнакомых: в Хаскелле типы являются перечисляемыми (ну, с некоторыми оговорками). В частном тривиальном случае эти перечисляемые типы вырождаются в нечто вроде джавовых енумов. Для удобства работы с ними в таком качестве (в частности, конверсии их в числовые типы) есть класс Enum, который провайдит некоторые полезные методы, и возможность на халяву сделать свой тип его инстансом (и получить эти методы в своё пользование). Это очень удобно, если есть вот такие вот типы и их надо часто переводить в числа (в порядке возрастания, как перечислены):

data LinkageType = External | Weak | Appending | Internal | LinkOnce | DllImport | DllExport |
ExternWeak | Common | Private | WeakOdr | LinkOnceOdr |
ExternallyAvailable | LinkerPrivate deriving (Bounded, Enum, Show, Eq)

data VisibilityType = DefaultVis | HiddenVis | ProtectedVis deriving (Bounded, Enum, Show, Eq)

Включение Enum в список deriving даёт нам возможность использовать методы toEnum и toInt для конверсии между нашими типами и Int соответственно. Есть только одна проблема - при переводе из Int никто не гарантирует, что число было в рамках наших типов, в противном случае программа будет убита с ошибкой (error), что не есть гуд. Казалось бы, что проще - нужно проверить число и возвращать, например, Maybe. И будет управляемо и наблюдаемо. Как раз для этого мы включили туда ещё и класс Bounded, который (тоже нахаляву) даёт нам методы minBound и maxBound с нижней и верхней границами типа соответственно.

beToInteger :: (Enum a, Bounded a) => a -> Integer
beToInteger = fromIntegral . fromEnum

ltFromInteger :: Integer -> Maybe LinkageType
ltFromInteger i = if inBound i then Just (toEnum $ fromInteger i)
else Nothing
where inBound ix = ix >= min && ix <= max
min = beToInteger (minBound::LinkageType)
max = beToInteger (maxBound::LinkageType)

vtFromInteger :: Integer -> Maybe VisibilityType
vtFromInteger i = if inBound i then Just (toEnum $ fromInteger i)
else Nothing
where inBound ix = ix >= min && ix <= max
min = beToInteger (minBound::VisibilityType)
max = beToInteger (maxBound::VisibilityType)

Как можно заметить, последние две функции абсолютно идентичны, отличаются только именами типов. Глупость? Глупость! Более того, наглая копипаста. Срочно генерализировать!

beFromInteger ::(Enum a, Bounded a) => Integer -> Maybe a
beFromInteger i = if inBound i then Just (toEnum $ fromInteger i)
else Nothing
where inBound ix = ix >= min && ix <= max
min = beToInteger (minBound::a)
max = beToInteger (maxBound::a)

Ура, ура. Я в цирке был. Но вот ghc считает иначе:

src/Bla/Bla/Bla.hs:77:43:
Could not deduce (Bounded a1) from the context ()
arising from a use of `maxBound'
atsrc/Bla/Bla/Bla.hs:77:43-50
Possible fix:
add (Bounded a1) to the context of an expression type signature
In the first argument of `beToInteger', namely `(maxBound :: a)'
In the expression: beToInteger (maxBound :: a)
In the definition of `max': max = beToInteger (maxBound :: a)

Подозреваю, что где-то я глобально не прав, но кто ж укажет мне путь истинный.
Вывод типов, my ass.

haskell

Previous post Next post
Up