Использование haskell read и typeclasses - ошибка переменной неоднозначного типа - PullRequest
1 голос
/ 22 июня 2010

У меня есть неоднозначная ошибка переменной типа в определении «испытания» ниже, мне интересно, есть ли что-нибудь, что можно сделать, чтобы заставить эту ситуацию работать? Я действительно хочу иметь дело только с экземплярами, а не с явными типами данных (такими как MO1, MO2, включенные ниже).

module Tc102 where

class (Show a, Read a) => MyObj a where
    alpha :: a->String
    beta  :: a->Int

data MO1 = MO1 { a1 :: String, b1 :: Int } deriving (Show,Read)
data MO2 = MO2 { a2 :: String, b2 :: Int } deriving (Show,Read)

instance MyObj MO1 where
    alpha = a1
    beta = b1

instance MyObj MO2 where
    alpha = a2
    beta = b2


a = MO1 "a" 3
b = MO2 "b" 4

test :: MyObj a => a->String
test = alpha


showMe :: (MyObj a)=> a -> String
showMe = show

readMe :: (MyObj a) => String -> a
readMe = read

trial :: MyObj a => a -> String
trial = test . readMe . showMe

заранее всем спасибо! Однако, боюсь, мне может понадобиться вспомогательная функция, которая преобразует старый ADT в «последние версии» ...

Simon

РЕДАКТИРОВАТЬ Для пояснения, представьте, что я сначала показываю в файл, а затем перезагружаю объект. Тогда функция, которая у меня есть, больше похожа на

trial :: String -> Int
trial s = beta x
  where x = readMe s

Ответы [ 3 ]

1 голос
/ 22 июня 2010

Вы получаете сообщение об ошибке, потому что компилятор не знает, какой конкретный тип readMe должен возвращать, поскольку все, что требуется test, - это то, что это экземпляр MyObj. Это может быть MO1, MO2 или что-то еще целиком, и readMe может вернуть любой из них.

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

trial :: MyObj a => a -> String
trial = test . readShowMe
    where readShowMe :: MyObj a => a -> a
          readShowMe = readMe . showMe

Теперь компилятор может выяснить, какой конкретный тип дать test в качестве ввода.

0 голосов
/ 22 июня 2010

В этом есть причудливые уловки, и окончательный ответ, действительно, состоит в том, чтобы прочитать какую-то экзистенциальную сущность.Для изящного изложения очень мощного / общего способа сделать это см. Заключительные примечания к лекции Олега Типпеда без тегов: http://okmij.org/ftp/tagless-final/course/#type-checking.

Но это серьезный перебор для примера, подобного вашему - хитрость обычно возникает, когда выимейте древовидные структуры, особенно если у вас есть древовидные структуры, представляющие лямбда-термины.

Для ваших целей приведенный выше пример примерно одинаков.

Но имейте в виду, что даже если вы можете создавать новые экземплярыMyObj, ваша функция чтения сможет читать только фиксированный юниверс, по крайней мере, без высокоуровневых хакеров.

Так что это тот случай, когда я задам вопрос, хотите ли вы начать с класса типов, илипросто больше конструкторов в одном ADT.

data MyObj = MO1 { a1 :: String, b1 :: Int }
           | MO2 { a2 :: String, b2 :: Int } deriving (Show,Read)

... например.

0 голосов
/ 22 июня 2010

Вы не можете прочитать тип времени компиляции из файла времени выполнения.Вы должны явно реализовать переменное значение времени выполнения.

Как пример:

data AnyMyObj = forall a . MyObj a => AnyMyObj a

test :: AnyMyObj -> String
test (AnyMyObj x) = alpha x

showMe :: AnyMyObj -> String
showMe (AnyMyObj x) = show x

readMe :: String -> AnyMyObj
readMe s = AnyMyObj (read s :: MO1) -- maybe something more complicated

trial :: AnyMyObj -> String
trial = test . readMe . showMe
...