2
votes

With functional dependencies I can constrain type of dependent parameter in a type class using multi-parameter type classes. Just like this:

{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses,TypeSynonymInstances  #-}

class (Num a, Integral b) => F a b | a -> b where
  f :: a -> b

instance F Int Int where
  f = id

instance F Float Integer where
  f = truncate

And everything'll work perfectly.

> f (1 :: Int)
1
> f (1.9 :: Float)
1

But if I try to write something like

instance F Double String where
  f = show

I'll get the following compilation error:

No instance for (Integral String)
arising from the superclasses of an instance declaration
Possible fix: add an instance declaration for (Integral String)
In the instance declaration for `F Double String'

Is there a way to approach this with type families instead of fundeps?

2

2 Answers

5
votes

I guess you want to have something like this:

{-# LANGUAGE TypeFamilies, FlexibleContexts  #-}
class Num a => G a where
  type B a
  g :: (Integral (B a)) => a -> B a

instance G Int where
  type B Int = Int
  g = id

instance G Float where
  type B Float = Integer
  g = truncate

This example is not directly using a type family, but an associated type synonym, which is activated by the same flag. The idea is fairly easy. Instead of explicit giving a second parameter to the typeclass, we define a type synonym, that is filled in with the appropriate type.

This is also my first time to use associated type synonyms, but it seems to be a quite cool feature.

12
votes

Basically not, and this actually has nothing to do with functional dependencies (or type families). Your class definition has

class (Num a, Integral b) => F a b

which declares that there must be an Integral instance for b. A String doesn't have an Integral instance, so you can't have anything of the form F a String unless you define

-- String is a type synonym for [Char]
instance Integral [Char] where

I don't know that this would be sensible in general. If it would make sense to create an Integral instance for Strings in your system, you would probably want to put a newtype wrapper around String and create the instance for that instead.