2012-01-24 4 views
1

Я сделал следующую программу haskell, которая будет выполнять некоторые основные операции загрузки, чтения и увеличения. Я получаю ошибку типа. Может кто-нибудь рассказать, почему существует ошибка типа и как я могу ее решить.Ошибка в программе haskell

module ExampleProblem (Value,read',load,incr) where 
newtype Value a = Value Int deriving (Eq,Read,Show) 

read':: Value Int -> Int 
read' (Value a) = a 

load:: Int -> Value Int 
load a = Value a 

incr:: Value Int -> Value Int 
incr (Value a) = Value (a+1) 


main = do 
     (Value ab) <- (load 42) 
     if (read'(Value ab) /= 42) 
     then show "Failure to load" 
     else do 
      Value b <- incr(Value ab) 
      Value c <- incr(Value b) 
      if ((Value c) == Value 44) 
      then show "Example finished" 
      else show "error" 
      return 

Ошибки я получаю:

Couldn't match expected type `Int' with actual type `Value t0' 
In the pattern: Value ab 
In a stmt of a 'do' expression: (Value ab) <- (load 42) 
In the expression: 
    do { (Value ab) <- (load 42); 
     if (read' (Value ab) /= 42) then 
      show "Failure to load" 
     else 
      do { Value b <- incr (Value ab); 
       .... } } 

И когда я сделал отдельный файл, в котором я написал основную функцию я получал ошибку масштаба, хотя я импортировал модуль ExampleProblem.

Not in scope: data constructor `Value' 
+0

5 вопросов и ответов не принято. Поработайте над этим, пожалуйста. – leppie

+1

Извините, я не знал об этом .. Сделали поправки сейчас :) –

ответ

5

Кажется, вы смущены о том, как использовать служебную запись. Do-notation используется для составления действий в монете . В случае main, эта монада будет IO, поэтому я буду придерживаться IO, чтобы все было просто.

Есть три типа высказываний, которые можно использовать в сделай нотации:

  1. x <- foo связывает результат выполнения действия foo шаблона x. foo должен иметь тип IO Something, а x будет иметь соответствующий тип Something.

  2. let x = foo связывает значение без каких-либо особых событий. Это то же самое, что и = на верхнем уровне, за исключением того, что все предыдущие привязки находятся в области видимости.

  3. foo осуществляет действие типа IO Something. Если это последний оператор в блоке do, это становится результатом блока, иначе результат игнорируется.

Основная проблема в вашем коде является то, что вы используете x <- foo заявления с вещами, которые не IO действия. Это не сработает. Вместо этого используйте форму let x = foo.

Во-вторых, show также не является IO действиями. Это просто функция, которая преобразует материал в String. Вероятно, вы использовали putStrLn, который выведет строку на стандартный вывод.

В-третьих, return не является утверждением, как на C или Java. Это функция, которая при задании производит действие, которое ничего не делает и возвращает значение. Он часто используется как последнее в do-блоке, когда вы хотите, чтобы он возвращал чистое значение. Здесь нет необходимости.

И, наконец, если вы хотите запустить этот код, ваш модуль должен быть вызван Main и он должен экспортировать функцию main. Самый простой способ сделать это - просто удалить строку module ... where, и имя по умолчанию будет Main. Обычно вам нужна эта линия только с модулями вашего проекта, которые не содержат main.

main = do 
    let Value ab = load 42 
    if read' (Value ab) /= 42 
     then putStrLn "Failure to load" 
     else do 
     let Value b = incr (Value ab) 
     let Value c = incr (Value b) 
     if Value c == Value 44 
      then putStrLn "Example finished" 
      else putStrLn "error" 

Это должно работать, но вы напрасно обертывание и разворачивание своих значений в Value типа. Возможно, вы намеревались что-то вроде этого:

main = do 
    let ab = load 42 
    if read' ab /= 42 
     then putStrLn "Failure to load" 
     else do 
     let b = incr ab 
     let c = incr b 
     if c == Value 44 
      then putStrLn "Example finished" 
      else putStrLn "error" 
2

Начну со второго вопроса.

newtype Value a = Value Int deriving (Eq,Read,Show) 

Это фактически создает дваValue S:

  • Value в newtype Value a является типа конструктор
  • Value в Value Int является конструкторомданные (обычно просто называют его конструктор)

Это разные вещи!

module ExampleProblem (Value,read',load,incr) where 

Здесь Value означает конструктор типа. Чтобы экспортировать конструктор данных, а также, что вам нужно

module ExampleProblem (Value(Value),read',load,incr) where 

Теперь о вашем первом вопросе.

  • main должен иметь тип IO something

Вы установили main быть do блок, так что это означает

  • все, чтобы справа от <- должен иметь тип IO somethingOrOther

Строка с сообщением об ошибке не

(Value ab) <- (load 42) 

load 42 имеет тип Value Int, очевидно, ничего общего с IO, так что вы получите сообщение об ошибке.

Итак, как вы это исправите?

  • если строка кода в do блок не является заявление IO, он должен быть подведенный Заявление о

Другие ошибки вам нужно исправить:

  • return не делает то, что делает на всех других языках. В частности, для возврата всегда требуется значение. Мы должны были назвать это чем-то другим. Извини за это. Представьте, что это называется pure. Так или иначе, вам здесь не нужно.
  • для печати String на экран, вы должны использовать putStrLn, а не show. Ghci печатает возвращаемое значение выражений, которые вы ему даете, но это программа сама по себе, , поэтому вам нужно сделать IO самостоятельно.
  • then и else нужно отступом дальше, чем if (я думаю, что это правило меняется, но я не думаю, что до сих пор)

Таким образом, мы в конечном итоге с

main = do 
     let Value ab = load 42 
     if read' (Value ab) /= 42 
      then putStrLn "Failure to load" 
      else do 
       let Value b = incr (Value ab) 
       let Value c = incr (Value b) 
       if Value c == Value 44 
       then putStrLn "Example finished" 
       else putStrLn "error" 

Сноски:

  1. Не совсем верно:Блокимогут использоваться для вещей, отличных от IO. Но об этом узнаете позже.
2

Еще одно замечание: вы должны стараться делать как можно больше работы вне основной вещи. В вашем случае это легко: у вас есть расчет, который не принимает аргументов и создает строку (которая должна быть распечатана). Если у вас были разные «результаты», вы могли бы использовать, например, Either, но здесь мы в порядке с String в качестве возврата типа.

Как указал хаммар, у него не так много смысла сопоставлять рисунок и восстанавливать Value, просто используйте значения как они есть, и используйте сопоставление образцов, только если вам нужно получить доступ к номеру внутри.

Тип Значение не обязательно должно быть полиморфным, если оно всегда обертывает только Int, поэтому я сбросил a. Иначе у вас есть что-то, называемое «фантомным типом», что возможно, а иногда и полезно, но определенно не здесь (конечно, если вы хотите иметь возможность обернуть произвольных типов, вы можете написать data Value a = Value a).

Так вот версия, которая делает только голый минимум в IO, и сохраняет все остальное чисто (и, следовательно, гибкий, проверяемым и т.д.):

data Value = Value Int deriving (Eq,Read,Show) 

read':: Value -> Int 
read' (Value a) = a 

load:: Int -> Value 
load a = Value a 

incr:: Value -> Value 
incr (Value a) = Value (a+1) 

main = putStrLn calc 

calc :: String 
calc = let ab = load 42 
     in if read' ab /= 42 then "Failure to load" else increaseTwice ab 

increaseTwice :: Value -> String 
increaseTwice v = let b = incr v 
         c = incr b 
        in if c == Value 44 then "Example finished" else "error" 

я не могу использовать Haskell здесь, так что я надеюсь, это работает ...

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