简体   繁体   中英

Ad hoc polymorphic functions

I have some data that I'd like to print (some is Maybe and some is not), and I'm trying to create a generic showField function as follows:

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

This is throwing a rigid type error:

• 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

I generally understand this error, but I don't understand if there's a way to achieve what I'd like to. I've also tried pattern matching rather than guards, but can't quite work that out either. Is there something that I could construct in this format that would work?

It looks like you're trying to construct an adhoc polymorphic function - a function whose definition varies according to its type.

A parametric polymorphic function does the same thing to all data types:

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

In Haskell, adhoc polymorphism is implemented using type classes:

class ShowField a where
  showField :: a -> Text

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

However there's no way to define an instance for "all other types other than Maybe a" with type classes, so you just have to define instances for the types you actually care about:

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

You can cut down on boilerplate by using -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

The error tells us:

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

Basically, this tells us that according to the type signature you provided, the type of the first parameter is forall a. Show a forall a. Show a (the ' forall a. ' bit is implied by the signature), which means that the first parameter can be any type that is an instance of Show . It is a rigid type variable because it is defined by an explicit type signature.

It also tells us:

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

By applying the functions isJust and isNothing — both of type Maybe a -> Bool — to the first parameter you are also claiming the type of the first parameter is Maybe a which is obviously not the same type as forall a. Show a => a -> Text forall a. Show a => a -> Text .

You can turn this into a correct program simply by removing the type signature for showField , but that won't have the behavior you desire — the inferred type signature will be (Show a) => Maybe a -> Text which obviously only accepts values of Maybe a (where a is also an instance of Show ).

In Haskell, you can't have a function that accepts values of both a and Maybe a ¹. Without more context, it's unclear what your actual goal is, but there is almost certainly a more idiomatic way to achieve it.

¹ Unless you have a type class that has instances for both a and Maybe a .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM