Theodora
Theodora

Reputation: 588

Define data type include constrained functions

I am trying to adopt the idea of ReaderT pattern in a simple production system.

In the original example data type Env is defined as:

data Env = Env
  { envLog :: !(String -> IO ())
  , ...
  }

I my implementation, I defined a typeclass Serializable to help me logging different data types. So that I could have a more generic logging function (including serialization in different file format):

data Env = Env
  { evnLog :: (Serializable a) => a -> FilePath -> IO ()
  , ...
  }

Of course, this doesn't work. The error message is:

    Not in scope: type variable ‘a’
   |
61 |   { envLog :: (Serializable a) =>
   |                             ^

If a is included in the Type constructor of Env, I will be forced to change the Context information in ReaderT constantly, it would be really cumbersome.

I just want to pass this generic function along a sequences of computations and use it whenever being needed.

Is there any possibility of not introducing a in the Type constructor of Env ? Or maybe ReaderT pattern needs more tricks to be used like this?

Upvotes: 3

Views: 98

Answers (1)

You're trying to use a rank-2 type (specifically, the type of the Env data constructor). You need to both enable the RankNTypes extension, and use a forall since rank-2 types are never inferred:

{-# LANGUAGE RankNTypes #-}

data Env = Env
  { envLog :: forall a. (Serializable a) => a -> FilePath -> IO ()
  , ...
  }

Upvotes: 7

Related Questions