2012-01-24 37 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

看起來你很困惑如何使用符號。註釋用於編寫monad中的操作。在main的情況下,該monad將爲IO,因此我將堅持使用IO以保持簡單。

有三種類型,您可以在DO-符號使用的語句:

  1. x <- foo結合運行的操作foo的模式x的結果。 foo必須具有類型IO Somethingx將具有相應的類型Something

  2. let x = foo綁定一個沒有任何特殊事件的值。這與頂層的=相同,區別在於前面的任何綁定都在範圍之內。

  3. foo運行類型爲IO Something的動作。如果它是do-block中的最後一個語句,則這成爲塊的結果,否則結果將被忽略。

在你的代碼的主要問題是,你正在使用x <- foo語句與東西不是IO行動。這是行不通的。而是使用let x = foo表單。

其次,show也不是IO的操作。它只是一個將東西轉換爲String的函數。您可能打算使用putStrLn這將打印一個字符串到標準輸出。

第三,return不是像C或Java中的語句。這是一個函數,它給出一個值,產生一個什麼也不做並返回值的動作。當你希望它返回一個純粹的值時,它通常被用作do-block中的最後一件事。這裏沒有必要。

最後,如果你想運行這個代碼,你的模塊必須被調用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:

  • newtype Value aValue類型構造
  • Value IntValue數據構造(通常叫它一個構造函數)

他們是不同的東西!

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的語句,它必須是一個let語句

其他錯誤需要修正:

  • return不會做任何其他語言的功能。 特別是,它總是會返回一個值。我們應該叫它別的東西。對於那個很抱歉。 想象一下,它被稱爲pure。無論如何,你不需要這裏。
  • 要在屏幕上打印String,您應該使用putStrLn而不是show。 Ghci打印出你給它的表達式的返回值,但這是一個程序本身,所以你必須自己去做IO。
  • thenelse需要進一步縮進比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. 並不完全正確:do塊可以用於IO以外的其他功能。但你稍後會了解這一點。
2

一個附加註釋:你應該嘗試做的主要-IO的事情儘可能外面很多工作。在你的情況下,很容易:你有一個計算,它不需要參數,併產生一個字符串(應該打印出來)。如果你有不同的「結果」,你可以使用例如Either,但這裏我們沒有問題,只是String作爲返回類型。

正如Hammar指出的那樣,模式匹配並沒有太多意義,並且一直重構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,所以我希望這項工作...