[Haskell-cafe] manage effects in a DSL

Dominique Devriese dominique.devriese at cs.kuleuven.be
Mon Feb 10 19:07:37 UTC 2014


Roman,

2014-02-10 18:22 GMT+01:00 Roman Cheplyaka <roma at ro-che.info>:
> * Daniel Trstenjak <daniel.trstenjak at gmail.com> [2014-02-10 17:10:01+0100]
>> On Mon, Feb 10, 2014 at 04:55:52PM +0100, Dominique Devriese wrote:
>> > Hm. Interesting point, I guess this is the same problem as the whole
>> > orphan instances debate...  I didn't think of the connection to that
>> > problem.  Still, I'm convinced there are situations where local
>> > instances are *exactly* what we need, so there must be some way to
>> > avoid this problem...
>>
>> If a type class has a clear semantical meaning, what should then
>> be the point of having multiple instances for the same data type?
>>
>> A clear semantical meaning contradicts multiple instances, they would
>> only make reasoning about your code harder.
>>
>> The few use cases where it might be nice to be able to define a new
>> instance aren't IMHO worth the drawbacks.
>>
>> You would just open Haskell for Ruby like monkey patching.
>
> How about a compromise. We already have a way to introduce fresh type
> names: using existential types. They look like good candidates for local
> instances (by definition, they can't have global instances, because the
> names themselves are necessarily local). Of course, you can define an
> instance only once, as for usual types.
>
> This would cover the case when "dynamic" instances are needed without
> compromising soundness. Example:
>
>   data IsoInt = forall a . IsoInt (Int -> a) (a -> Int)
>
>   foo modulus =
>     case IsoInt id id of
>       IsoInt (fromInt :: Int -> a) toInt ->
>         let
>           eqMod x1 x2 = (toInt x1 - toInt x2) `mod` modulus == 0
>
>           -- note: a is rigid here
>           instance Eq a where
>             (==) = eqMod
>         in ...

Just a note that your proposal seems very related to what Kiselyov and
Shan propose in their paper on "Implicit Configurations"
(http://dl.acm.org/citation.cfm?id=1017481).

Anyway, I still think that there are cases where I want to say that I
want to use "real" local instances.

Another interesting example by the way is instancing MonadState for
IO.  Consider how every IORef a can be used to build a MonadState a
instance for IO:

  data MonadStateD a m = MonadStateD { putM :: a -> m (), getM :: m a }
  ioRefStateD :: IORef a -> MonadStateD a IO
  ioRefStateD ref = MonadStateD (writeIORef ref) (readIORef ref)

I have the feeling that Brandon's argument (that multiple instances
cannot be permitted because, for example, Data.Map should only be used
with a single Ord instance) in some way comes down to solving a lack
for an ML-like module system by imposing an otherwise artificial
restriction on type classes:  to enforce that a type class instance is
unique within a module parameterised by it, we simply require that the
instance be globally unique...

That being said, Brendan is clearly right that we can't just drop a
guarantee that is widely relied upon.

Regards,
Dominique


More information about the Haskell-Cafe mailing list