2017-09-26 51 views
2

我有一些數據,我想打印(有的也許還有一些不算),我試圖按照以下步驟創建一個通用的showField功能:特設多態函數

showField :: (Show a) => a -> Text 
showField x 
    | isJust x = Text.pack $ show $ fromJust x 
    | isNothing x = "None" 
    | otherwise = Text.pack $ show x 

這是拋出一個剛性類型的錯誤:

• Couldn't match expected type ‘Maybe a0’ with actual type ‘a’ 
    ‘a’ is a rigid type variable bound by 
    the type signature for: 
     showField :: forall a. Show a => a -> Text 
    at /data/users/jkozyra/fbsource/fbcode/experimental/jkozyra/hs/holdout_cleanup/HoldoutReaper.hs:244:18 
• In the first argument of ‘isNothing’, namely ‘x’ 
    In the expression: isNothing x 
    In a stmt of a pattern guard for 
       an equation for ‘showField’: 
    isNothing x 

我大致明白這個錯誤,但如果有一種方法來實現想要什麼,我要我不明白。我也嘗試過模式匹配而不是守衛,但也不能完全解決這個問題。有沒有什麼我可以用這種格式來構建的?

+0

可能重複的[剛性類型變量錯誤](https://stackoverflow.com/questions/4629883/rigid-type-variable-error) – jberryman

+3

儘管錯誤匹配,我不願意將其標記爲重複;在提議的複製中對問題的解釋與這裏對問題的解釋完全不同。 –

+0

@DanielWagner是的,我不認爲這是重複的,因爲我的問題是關於創建一個adhoc多態函數。我提供了一個錯誤來解釋我知道我寫的代碼沒有工作。 – jek339

回答

11

它看起來像你試圖構建一個adhoc多態函數 - 一個函數的定義因類型而異。

參數化多態函數做同樣的事情到所有的數據類型:

class ShowField a where 
    showField :: a -> Text 

instance Show a => ShowField (Maybe a) where 
    showField Nothing = "None" 
    showField (Just a) = Text.pack $ show a 

但是沒有辦法來定義一個實例:

both :: a -> (a,a) 
both a = (a,a) 

在Haskell,特設多態是用類型類實現對於「類型類別以外的所有其他類型以外的其他類型」,因此您只需要爲您實際關心的類型定義實例:

class ShowField Int where 
    showField = Text.pack . show 
class ShowField Float where 
    showField = Text.pack . show 

您可以通過使用-XDefaultSignatures削減樣板:

class ShowField' a where 
    showField :: a -> Text 
    default showField :: Show a => a -> Text 
    showField = Text.pack . show 

instance ShowField' Int where 
instance ShowField' Float where 
+0

好吧,有兩種方法,但我懷疑這裏是否合適。 – dfeuer

+0

請注意,'showField'簽名與默認簽名不同。它看起來很不幸,你需要爲每種類型顯式實例化'ShowField''。 –

+0

@StefanHanke,好吧,你*可以*使用重疊的實例,或者你可以*使用一個使用封閉類型家族構建的手動「實例鏈」。但我認爲這些可能不合適。 – dfeuer

1

錯誤告訴我們:

‘a’ is a rigid type variable bound by 
    the type signature for: 
    showField :: forall a. Show a => a -> Text 

基本上,這告訴我們,根據您提供的類型簽名的類型第一個參數是forall a. Show a('forall a.'位被簽名隱含),這意味着第一個參數可以是任何類型,即Show的實例。這是一個剛性類型變量,因爲它是由顯式類型簽名定義的。

這也告訴我們:

Couldn't match expected type ‘Maybe a0’ with actual type ‘a’ 

通過應用功能isJustisNothing - 無論Maybe a -> Bool型的 - 你也聲稱第一個參數的類型,第一個參數是Maybe a這顯然是不與forall a. Show a => a -> Text相同。

你可以把它變成一個正確的程序簡單地通過除去showField類型簽名,但不會得到你想要的行爲 - 推斷出的類型簽名會(Show a) => Maybe a -> Text這顯然只接受Maybe a值(其中a是也是Show的一個實例)。

在Haskell中,您不能具有接受aMaybe a¹的值的函數。沒有更多的上下文,目前還不清楚你的實際目標是什麼,但幾乎肯定有一種更習慣的方式來實現它。

¹除非您有一個類型爲aMaybe a的實例。