2013-10-22 149 views
4

縮寫形式(這將解決我的問題的一個方法至少)如何查看類型是否是Haskell中的類的實例?

我怎樣才能做這樣的事情:

try_to_show :: a -> String 
try_to_show val = if (val is instance of Show) (show val) else "Cannot show" 

我可能做這完全錯了(的unhaskell方式) ;我只是在學習,所以請讓我知道是否有更好的方法來解決這個問題。

上下文:我在寫一堆樹結構。我想重用我的prettyprint函數二叉樹。儘管不是所有的樹都可以使用通用的Node/Branch數據類型;不同的樹需要不同的額外數據。因此,要重用prettyprint功能我想創建一個類不同的樹會的實例:

class GenericBinaryTree a where 
    is_leaf :: a -> Bool 
    left :: a -> a 
    node :: a -> b 
    right :: a -> a 

這樣,他們只需要實現方法來檢索左,右,以及當前節點的值,prettyprint沒有按」 t需要了解內部結構。

然後我得到了到這裏:

prettyprint_helper :: GenericBinaryTree a => a -> [String] 
prettyprint_helper tree 
    | is_leaf tree = [] 
    | otherwise = ("{" ++ (show (node tree)) ++ "}") : (prettyprint_subtree (left tree) (right tree)) 
     where 
      prettyprint_subtree left right = 
       ((pad "+- " "| ") (prettyprint_helper right)) ++ ((pad "`- " " ") (prettyprint_helper left)) 
      pad first rest = zipWith (++) (first : repeat rest) 

我也得到了Ambiguous type variable 'a0' in the constraint: (Show a0) arising from a use of 'show'錯誤(show (node tree))

這裏是最基本的樹數據類型和實例的定義(我的其他樹木有其他領域的例子但他們無關的通用prettyprint功能)

data Tree a 
    = Branch (Tree a) a (Tree a) 
    | Leaf 
instance GenericBinaryTree (Tree a) where 
    is_leaf Leaf = True 
    is_leaf _ = False 
    left (Branch left node right) = left 
    right (Branch left node right) = right 
    node (Branch left node right) = node 

我可以定義node :: a -> [String]並處理樹的每個實例/類型的字符串化,但是這感覺更整潔。根據prettyprint,我只需要一個字符串表示法,但如果稍後添加其他通用二叉樹函數,我可能需要實際值。

那麼我該如何編寫這個工作節點值是否是Show的一個實例呢?或者還有什麼其他方式可以解決這個問題?在面向對象的語言中,我可以很容易地檢查一個類是否實現了一些東西,或者一個對象是否有方法。


,因爲它不是一個需要showable樹,我不能使用類似

prettyprint :: Show a => a -> String 

,這是一個需要showable樹(按功能node返回)內的值。我也嘗試將node改爲Show b => a -> b而沒有運氣(還有一堆其他類型的class/preconditions /不管/我甚至不知道我在做什麼)。

+0

,你可以讓一切都顯示的實例:'實例顯示,其中顯示_ =「」' – aavogt

回答

2

在你的班級中你有node :: a -> b其中a被固定爲例示GenericBinaryTreeb是......真正的任何東西。完全沒有任何限制,最終會得到一個根本無法使用的值,更不用說show

node :: Show b => a -> b方法提供了一種約束b必須Show能夠後來你遇到了第二個問題:我們仍然不知道,具體是什麼b是!

特別是,這就是爲什麼在class聲明中使用不受約束的類型變量的好主意。你看他們在的地方,如

instance Alternative f where 
    empty :: f a 

表明,我們必須創建一個f容器,其內部具有零種元素的能力......所以還不如採取任何類型f a任何a任何責任。

一個解決辦法可能是使用MultiParamTypeClasses

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-} 

class GenericBinaryTree b a | a -> b where 
    is_leaf :: a -> Bool 
    left :: a -> a 
    node :: a -> b 
    right :: a -> a 

showNode :: (Show b, GenericBinaryTree b a) => a -> String 
showNode = show . node 

編譯就好了。

+0

呀看到我的最後一段我已經嘗試過,不工作 – Raekye

+0

編輯來考慮下一個錯誤。 –

+0

有趣。那麼我該如何定義實例?我嘗試過'實例GenericBinaryTree(樹a)a ...',它說「非法實例聲明...」,我在兩個文件中都包含了pragma。另外,是否有一種方法可以在沒有MultiParamTypeClasses的情況下完成一般目標?是否有解決「簡單形式」問題的方法(請參見問題的頂部) – Raekye

4

你不能在你的問題中提到的第一個解決方案。你能做的就是儘量類似:

class GenericBinaryTree t where 
    is_leaf :: t a -> Bool 
    left :: t a -> t a 
    node :: t a -> a 
    right :: t a -> t a 

prettyprint_helper :: (GenericBinaryTree f, Show a) => f a -> [String] 

這裏我們使用類型類中指定的方式樹可以導航,這能解決您的問題,樹可以有不同的結構。接下來的問題是如何顯示節點值,這是通過在prettyprint_helper singnature中添加Show類型約束來解決的。 GenericBinaryTree實例執行:

instance GenericBinaryTree Tree where 
    is_leaf Leaf = True 
    is_leaf _ = False 
    left (Branch left node right) = left 
    right (Branch left node right) = right 
    node (Branch left node right) = node 
+0

看起來不錯,現在唯一的問題是我得到'''''prettyprint(Leaf)= GenericBinaryTree.prettyprint Leaf'因爲'Leaf'構造函數沒有任何東西來標識它的類型參數,所以''configuous type variable in constraint'...怎麼可以我解決這個問題? – Raekye

+0

由於'Leaf'類型不需要顯示,是否有添加某種「或者」約束的方法?既然它可能是一個Leaf,我們不需要顯示它,否則它將成爲一個分支,在這種情況下,第二個類型參數需要顯示。 – Raekye

+0

@Raekye:你可以添加'prettyprint'的代碼,以便你的評論更清晰嗎? – Ankur

相關問題