Безопасный и полиморфный toEnum

Я хотел бы записать безопасную версию toEnum:

 safeToEnum :: (Enum t, Bounded t) => Int -> Maybe t

Наивная реализация:

safeToEnum :: (Enum t, Bounded t) => Int -> Maybe t
safeToEnum i =
  if (i >= fromEnum (minBound :: t)) && (i <= fromEnum (maxBound :: t))
    then Just . toEnum $ i
    else Nothing

main = do
  print $ (safeToEnum 1 :: Maybe Bool)
  print $ (safeToEnum 2 :: Maybe Bool)

И это не работает:

safeToEnum.hs:3:21:
    Could not deduce (Bounded t1) from the context ()
      arising from a use of `minBound' at safeToEnum.hs:3:21-28
    Possible fix:
      add (Bounded t1) to the context of an expression type signature
    In the first argument of `fromEnum', namely `(minBound :: t)'
    In the second argument of `(>=)', namely `fromEnum (minBound :: t)'
    In the first argument of `(&&)', namely
        `(i >= fromEnum (minBound :: t))'

safeToEnum.hs:3:56:
    Could not deduce (Bounded t1) from the context ()
      arising from a use of `maxBound' at safeToEnum.hs:3:56-63
    Possible fix:
      add (Bounded t1) to the context of an expression type signature
    In the first argument of `fromEnum', namely `(maxBound :: t)'
    In the second argument of `(<=)', namely `fromEnum (maxBound :: t)'
    In the second argument of `(&&)', namely
        `(i <= fromEnum (maxBound :: t))'

А также я понимаю сообщение, компилятор не распознает это minBound и maxBound должен произвести точно тот же тип как в типе результата safeToEnum inspite явного описания типа (:: t). Какая-либо идея, как зафиксировать его?


Решенный

И работа решений camccann и Dave (хотя Dave нужно быть скорректирован). Спасибо вам обоим (но я мог принять только один). Рабочий пример с ScopedTypeVariables:

{-# LANGUAGE ScopedTypeVariables #-}

safeToEnum :: forall t . (Enum t, Bounded t) => Int -> Maybe t
safeToEnum i =
  if (i >= fromEnum (minBound :: t)) && (i <= fromEnum (maxBound :: t))
    then Just . toEnum $ i
    else Nothing
8
задан sastanin 30 April 2010 в 13:22
поделиться

2 ответа

Переменные типа с областью видимости здесь не нужны, вам просто нужно дать понять GHC, что вы ожидаете, что все элементы Enum будут быть одного типа. Это легко сделать, передав их все функции, которая явно принимает различные Enum одного типа. Вот один из способов:

enumIfBetween :: (Enum a) => a -> a -> Int -> Maybe a
enumIfBetween a z x = let a' = fromEnum a
                          z' = fromEnum z
                      in if a' <= x && x <= z'
                         then Just $ toEnum x
                         else Nothing

safeToEnum i = enumIfBetween minBound maxBound i

main = do
    print $ (safeToEnum 1 :: Maybe Bool)
    print $ (safeToEnum 2 :: Maybe Bool)

Попробовать в GHCi:

> main
Just True
Nothing

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

safeToEnum :: (Enum t, Bounded t) => Int -> Maybe t
safeToEnum i = let r = toEnum i
                   max = maxBound `asTypeOf` r
                   min = minBound `asTypeOf` r
               in if i >= fromEnum min && i <= fromEnum max
               then Just r
               else Nothing

Эта версия также работает.

Имейте в виду, что ScopedTypeVariables - это расширение языка, поэтому его не обязательно переносить между компиляторами. На практике почти все используют GHC, но обычно предпочитают придерживаться стандартного базового языка (например, Haskell 98), когда это возможно. В этом случае ScopedTypeVariables действительно перебор; вики Haskell предлагает asTypeOf в качестве переносимой замены для такого рода сценариев.

13
ответ дан 5 December 2019 в 10:39
поделиться

Вам нужно использовать переменные с ограниченным типом

{-# LANGUAGE ScopedTypeVariables #-}

safeToEnum :: (Enum t, Bounded t) => Int -> Maybe t
safeToEnum i =
  if (i >= fromEnum (minBound :: t)) && (i <= fromEnum (maxBound :: t))
    then Just . toEnum $ i
    else Nothing

main = do
  print $ (safeToEnum 1 :: Maybe Bool)
  print $ (safeToEnum 2 :: Maybe Bool)

Без него t означает для всех t. т .

3
ответ дан 5 December 2019 в 10:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: