Вопрос

I was wondering if there is any way (perhaps with Type Families) of defining a Buildable type class with a build method such that something like this would work:

>>> import qualified Data.ByteString.Builder as B (Builder)
>>> import qualified Data.Text.Lazy.Builder as T (Builder)

>>> -- This should internally use `int8` from `Data.ByteString.Builder`:
>>> build (8::Int8) :: B.Builder

>>> -- This should internally use `decimal` from `Data.Text.Lazy.Builder`:
>>> build (8::Int8) :: T.Builder

>>> -- If no concrete type is specified, I'd expect something like this:
>>> :t build (8::Int8)
GBuilder Int8

I know how to make it work with MPTCs but then I have to explicitly define the type of the values to be built and the types of the builders. Is there any way to have the type system automatically pick the expected type without passing it explicitly?

Это было полезно?

Решение

Well, the evident solution would be the following:

class Builder b where
  type Element b
  build :: Element b -> b

instance Builder B.Builder where
  type Element B.Builder = Int8
  build = B.int8

instance Builder T.Builder where
  type Element T.Builder = Int8
  build = T.decimal

However the problem with this is that type families can't be used to determine the instance. So in this case the instance can only be determined by the b from build :: Element b -> b, but not by the Element b. This is useful to know, when dealing with typechecker errors.

Due to what I've just mentioned in many cases only data families can be used. The standard approach is to provide a newtype wrapper. E.g.:

class Builder b where
  data Element b
  build :: Element b -> b

instance Builder B.Builder where
  newtype Element B.Builder = BElement Int8
  build (BElement a) = B.int8 a

In this case the instance can be determined from both the b and the Element b of build :: Element b -> b.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top