Can “overloading” via FlexibleInstances return different types, or match on typeclasses?

对着背影说爱祢 提交于 2019-12-05 18:57:37

Is it possible to make the Integer version of adjust return an Integer, and the Double version return a Double?

You can make the Adjustable type class accept two type parameters instead of one, so it will know what's inside the AdjusterType:

{-# LANGUAGE MultiParamTypeClasses #-}

class Adjustable f a where
    adjust :: f a -> a

Then the instances should be:

instance Adjustable AdjusterType Int where
    adjust (Adjuster a) = a + 20

instance Adjustable AdjusterType Double where
    adjust (Adjuster a) = a + 0.04

And some results from ghci:

> :set +t

> adjust (Adjuster (100 :: Int))
< 120
< it :: Int
> adjust (Adjuster (100 :: Double))
< 100.04
< it :: Double

What type was it expecting here that Integer isn't matching...or would no type actually work and it's just a weird error message?

The return type of adjust is of type forall n . Num n => n, a polymorphic type with a single constraint Num, so your function returning a concrete type won't type check. Wrap your function with fromIntegral will solve the problem since fromIntegral :: (Integral a, Num b) => a -> b.

Is there any way to provide an overloaded behavior like above to a type class, or must it always be to a specific instance?

If you expect the function to behave differently for every distinct type, yes you have to add an instance for each. You may add some default behavior though, by restricting the type parameters of the class:

{-# LANGUAGE DeriveFunctor         #-}
{-# LANGUAGE MultiParamTypeClasses #-}

class Extract f where
  extract :: f a -> a

class (Extract f, Functor f, Num a) => Adjustable f a where
  adjust :: f a -> a
  adjust = extract . fmap (+20)

data AdjusterType a = Adjuster a
  deriving (Functor)

instance Extract AdjusterType where
  extract (Adjuster a) = a

instance Adjustable AdjusterType Int where
-- don't have to write any code here

A solution using type families, in particular associated data types, is the following:

{-# LANGUAGE TypeFamilies, FlexibleInstances #-}

class Adjustable a where
    type Elem a :: *
    adjust :: a -> Elem a


data AdjusterType a = Adjuster a
    deriving (Show)


instance Adjustable (AdjusterType Integer) where
    type Elem (AdjusterType Integer) = Integer

    adjust (Adjuster a) = a + 20

instance Adjustable (AdjusterType Double) where
    type Elem (AdjusterType Double) = Double

    adjust (Adjuster a) = a + 0.04

main = do
    let x = Adjuster 1 :: AdjusterType Integer
        y = Adjuster 1 :: AdjusterType Double
    print $ adjust x
    print $ adjust y

It compiles and the output is:

21
1.04
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!