2012-06-21 19 views
4

解析變量類型的元素,我終於設法操縱我的Haskell程序,以便在盡頭,如果我寫在Haskell

let foo = bar :: A 

然後我得到一個行爲,如果我寫

let foo = bar :: B 

然後我得到其他所需的行爲。

現在我想讓我的程序能夠在運行時解析這個參數,但我真的不知道如何繼續。有什麼建議?


編輯:我想分析某種(文本)配置文件對此我無以彌補規格/格式。
一個可能的玩具實例讀取一個整數作爲任一個Int或雙人上進一步上下文根據在配置文件中設置,沿着配置文件

barType: Int 
barValue: 2 

在以下的行給我巴= 2的東西:: Int,和

barType: Double 
barValue: 2 

給我吧= 2 ::雙。在這種情況下,我應該能夠接受任何具有Num實例的類型。
在我的情況,我有一個類的類與一些方法,我想解析任何與該類型的實例;這些方法可以根據確切的類型做出明顯不同的事情。我不知道如何去編寫一個Read實例。

謝謝。

+0

你的意思是你想根據運行時輸入在'A'和'B'類型之間進行選擇嗎? – Heatsink

+0

是的,這是完全正確的。 我不能僅僅解析它到任何一個B中,因爲我無法編寫這種類型所需的必要實例;此外還有兩種以上的興趣類型,我不想列舉所有可能性,而是希望接受正確的類型課程。 – Will

回答

2

如果您正在尋找基於實例化類型的不同行爲,那麼您需要一個類型類。很好的例子是Data.Binary甚至Read類型的實例。

E.g.

class Binary t where 

    -- | Decode a value in the Get monad 
    get :: Get t 

你沒有說你想做什麼樣的解析 - 文本?二進制? - 但它可能就像重用現有的解析庫併爲您的類型編寫實例一樣簡單。

或者,您可以使用解析器組合器來選擇不同的選項。

+0

不幸的是,我沒有足夠的能力來正確理解我的意思是從你的答案中做什麼。我在這個問題中添加了一些具體細節,希望能夠闡明我的目標。 – Will

2

我理解它的配置文件指定了解析器如何進一步進行。一個非常簡單的例子就是CSV,其中第一行確定所有後續行中的字段數。假設所有字段都是字符串字段我的變體。將第一行解析成它分析如下的解析器:

csvHeader :: Parser (Parser [String]) 

csvHeader結果是一個解析器將要被應用到所有的剩餘行:

csvHeader >>= many 

現在在你的情況下,實際結果也可以是多種類型之一。要做到這一點,最簡單的方法是有可能的結果的ADT:

data CsvField = Coordinate Float Float | Person Name 

更先進,更安全,更靈活的解決方案是不是想在數據方面,但在操作方面可以執行在他們。假設你正在解析一個包含一個數字(一個整數或一個浮點數)的配置文件。無論哪種情況,您都希望將該號碼打印到屏幕上。因此,而不是返回的實際數量,返回操作:如果你想保持約無論是包含在該領域,同時保持類型變量的一些信息

config :: Parser (IO()) 

最後,你需要成爲生存:

data Field = forall a. Field a (a -> a) 

現在你可以隨心所欲解析的東西,只要你有一個函數一起返回一個值,以適用於它:

config :: Parser Field 
+1

在你的例子中,我會說'CsvField' ADT是迄今爲止最好的方法。任何假定您在原始輸入上執行特定操作(例如打印到屏幕上)的替代方法都比ADT方法本身*少*靈活,ADT方法自然地將數據與適用於該操作的操作分離。 – mergeconflict

3

如果你這樣做,我相信你有一些代碼的內容取決於foo的類型,但其返回類型不是。假設你的完整代碼示例是

let foo = bar :: Int 
in print (foo + 1) 

運行時參數傳遞通常由函數完成,所以首先將代碼重寫爲函數。類型簽名中的(Show a, Num a) =>顯示某些類型的類方法作爲隱藏參數傳遞給該函數。

printBar :: (Show a, Num a) => a -> IO() 
printBar x = print ((bar `asTypeOf` x) + 1) 

例如,printBar (undefined :: Double)將在它的身上使用類型Double。編譯器發現該參數的類型爲Double,並且它傳入了Double的類型方法。

現在,我們來編寫一個函數,選擇printBar的實際類型。現在

invokeBar :: Bool -> (forall a. (Show a, Num a) => a -> b) -> b 
invokeBar True f = f (undefined :: Int) 
invokeBar False f = f (undefined :: Double) 

你可以打電話invokeBar True printBarinvokeBar False printBar選擇是否使用IntDouble

您無法從文件中讀取「未知類型」,因爲沒有包含全部可能的類型的文件格式。但是,您可以創建一個解析器來識別您將使用的類型的名稱,並使用上述方法來處理每個名稱。

1

使用您的示例輸入:

barType: Int 
barValue: 2 

由於barType告訴你如何解釋barValue,你需要在同一時間中的解析在兩行中,或保持狀態(使用StateStateT)在解析。無論哪種情況,您基本上都需要在類型上進行模式匹配。例如,如果我用Data.Binary.Get用於解析,我有這樣的:

data Bar = IntBar Int | DoubleBar Double 

parseBar :: String -> Get Bar 
parseBar t = case t of 
    "Int" -> liftM IntBar $ get 
    "Double" -> liftM DoubleBar $ get 

點是,無論你如何切它,你需要知道你會隨着時間提前合作什麼類型的,因爲你的解析器需要考慮它們。這也意味着類型類代表所有可能的類似事情可能不是要走的路;你可能需要一個簡單的BarADT。

+0

嗯,我絕對想要一個表示可能的Bar的類型類;這就是程序其餘部分的工作原理。然而,你是正確的,它似乎不適合解析。 有沒有辦法我可以做類似parseBar ::(存在a。數字a)=>字符串 - > a(避免ADT)類似的代碼如上? – Will