Typeclasses and GADTs

I'm putting together a geometry library in haskell. I'm not intending to release it, it's just a project that I'm using to improve my knowledge of the language.

I have a Local datatype, with the following definition

data Local a where
    MkLocal :: (Vectorise a) => ReferenceFrame -> a -> Local a

A reference frame is a Vector pointing to the origin of the frame and an angle representing the rotation of the frame, both defined wrt the "absolute" frame of reference (hey, it's not the real world!). A Vectorise geometry is one which has an invertible transformation into a list of Vector .

It occurred to me that Local could be an instance of Functor as follows:

instance Functor Local where
     fmap f geom = localise (frame geom) (f $ local geom)

but the compiler complains that there is no instance of Vectorisable for the use of localise in the definition. Is there any way around this limitation using one of the myriad GHC extensions?

EDIT: As requested in the comments, here are some of the types used

local :: Local a -> a
frame :: Local a -> ReferenceFrame
localise :: (Vectorise a) => ReferenceFrame -> a -> Local a

The error is

No instance for (Vectorise b)
  arising from a use of `localise'
In the expression:
  localise (frame geom) (f $ local geom)
In an equation for `fmap':
    fmap f lgeom = localise (frame geom) (f $ local geom))
In the instance declaration for `Functor Local'

Which makes sense, because the type for fmap is (a -> b) -> fa -> fb . It can infer that a must be an instance of Vectorise , but I was wondering how it could infer that b was, unless I could specify (somehow) I could tell the compiler that f must have the restricted return type without defining another typeclass when there is already one that almost fits the bill already (or alternately, if someone could helpfully explain why restricting classes in this way would break type inference somehow).

ps. I also fixed up a typo where I had reversed local and frame reversed in the definition of fmap


The problem is that localise requires its second argument to have type Vectorise a => a , but when you apply f (which has type a -> b ) to the result of local (of type Vectorise a => a ), there is no guarantee that the resulting value will have type that is an instance of Vectorise . What you really want is a an analog of Functor that works only on types that have a Vectorise constraint.

Until recently, it wasn't possible to define such type classes. This is a well-known problem and the reason why Data.Set has no Functor or Monad instance. However, with the recent ConstraintKinds GHC extension such "restricted functors" finally became possible:

{-# LANGUAGE GADTs, ConstraintKinds, TypeFamilies #-}
module Test
       where

import GHC.Exts (Constraint)

data ReferenceFrame = ReferenceFrame

class Vectorise a where
  ignored :: a

data Local a where
    MkLocal :: ReferenceFrame -> a -> Local a

local :: Vectorise a => Local a -> a
local = undefined

frame :: Local a -> ReferenceFrame
frame = undefined

localise :: (Vectorise a) => ReferenceFrame -> a -> Local a
localise = undefined

class RFunctor f where
  type SubCats f a :: Constraint
  type SubCats f a = ()
  rfmap ::  (SubCats f a, SubCats f b) => (a -> b) -> f a -> f b

instance RFunctor Local where
  type SubCats Local a = Vectorise a
  rfmap f geom = localise (frame geom) (f $ local geom)

You can read more about ConstraintKinds here and here.

链接地址: http://www.djcxy.com/p/43472.html

上一篇: Rust和Haskell中的类型类的特性有什么区别?

下一篇: 类型类和GADT