Type Family Type Hackery

I'm trying to write a fairly polymorphic library. I've run into a situation that's easier to show than tell. It looks a bit like this:

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
import Data.Map (Map)
import qualified Data.Map as Map

class Format f where type Target f
class Format f => Formatter x f where
  target :: forall y. Formatable y => Target f -> x -> y
class Formatable y where name :: y -> String

instance Formatable Integer where name = show
instance Formatable Int where name = show

split :: forall x f. (Format f, Formatter x f) => x -> f -> String -> [Either String (Target f)]
split = undefined

display :: forall x f. (Format f, Formatter x f) => f -> String -> x -> String
display f str x = let
  chunks = split x f str
  built = foldr apply "" chunks
  apply (Left s) accum = accum ++ s
  apply (Right t) accum = accum ++ name (target t x)
  in foldr apply "" chunks

Essentially, we have polymorphic Format s, which define a number of Target s. There are also a number of Formattable objects, which know how to respond to a bunch of different format options (reduced here to simply name ).

These Formattables are composed in a variety of ways, and can respond to a number of different targets. Formatter s are essentially the router between Format and Formattable -- given a target (from a specific format) they respond with a suitable Formattable object.

This is all pretty abstract. Here's an example:

  • DateFormat specifies targets like Year , Month , and Day .
  • MonthType is a Formattable newtype of Int that has names such as "February"
  • There is also the simple instance Formattable Int where name = show
  • DateTime might be a type synonym for (Int, MonthType, Int) .
  • (Obviously, I've cut out a lot of machinery here, such as piping the correct values around, but you get the idea.)

    The display function is fairly simple. It takes a formatter, a string specifying the format, an object to display, and renders it all into a string.

    First it breaks the string up into targets and strings. For example, a date formatter might break the string "%Y-%m-%d" into [Right Year, Left "-", Right Month, Left "-", Right Day] . The split function does that, and has been redacted here.

    The display function simply tracks down the Formattable s for each target and accumulates the string.

    Or, at least, it`s supposed to.

    But it fails typechecking with the following error:

    Reduced.hs:20:16:
        Could not deduce (Target f ~ Target f0)
        from the context (Format f, Formatter x f)
          bound by the type signature for
                     display :: (Format f, Formatter x f) => f -> String -> x -> String
          at Reduced.hs:(19,5)-(24,30)
        NB: `Target' is a type function, and may not be injective
        Expected type: [Either [Char] (Target f0)]
          Actual type: [Either String (Target f)]
        In the return type of a call of `split'
        In the expression: split x f str
        In an equation for `chunks': chunks = split x f str
    Failed, modules loaded: none.
    

    and I can't for the life of me figure out why. What am I doing wrong?


    The problem is that Target f does not determine f , which means that the function

    target :: (Formatter f x, Formatable y) => Target f -> x -> y
    

    can never be called. No matter what type annotation you give to target , you can't nail down what f is, and so the compiler can never figure out which Formatter instance to use. I'm not 100% sure, but probably the solution is not to use multi-parameter type classes and to let one of x or f be a function of the other. Also, you should probably just delete the Format class entirely (did you know you don't need a class to use a type family?). Perhaps something like this:

    class Formatter x where
        type Format x
        target :: Formatable y => Format x -> x -> y
    
    链接地址: http://www.djcxy.com/p/78244.html

    上一篇: 'type family'vs'data family',简而言之?

    下一篇: 类型家庭类型的黑客