Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: Using one class in instance of another

Tags:

haskell

I have a code to the following scheme:

class First s where
  func1 :: s -> s
class Second a where
  func2 :: s -> a s

data D s = D {value :: s}
myFunc2 :: First s => s -> D s
myFunc2 = undefined

In common func2's argument could not be instance of First. I want to make D instance of Second in only that cases when it's value instance of First. Then I want to get this instance:

instance Second D where
  func2 = myFunc2

But I get an error:

No instance for (First s)
  arising from a use of `myFunc2'

Okay, let instance be:

instance First s => Second D where
  func2 = myFunc2

But this gives error:

Ambiguous constraint `First s'
  At least one of the forall'd type variables mentioned by the constraint
  must be reachable from the type after the '=>'
In the instance declaration for `Second D'

So, is there a way to get instance of class with some conditions from other classes, but without all type variables after '=>'?

like image 370
spontaliku Avatar asked Dec 12 '22 04:12

spontaliku


2 Answers

I think it helps to think about what is meant qualitatively by

class Second a where
    func2 :: s -> a s

A Second instance promises that func2 is defined for any type s. But that's not true of myFunc2, because myFunc2 is only defined for those s for which there exists a First instance. This means that as you have defined First and Second, it is not possible to use myFunc2 in a Second instance (unless there existed a catch-all forall s . First s instance, but I'm assuming not, or you wouldn't have bothered to make a typeclass).

So, you will have to change at least one thing. You could redefine Second as Grzegorz suggested. If you don't like that, you could redefine Second like

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

class Second a s where
  func2 :: s -> a s

instance First s => Second D s where
  func2 = myFunc2

Note that this is saying something different than what you wrote originally, because now a Second instance does not guarantee that func2 is polymorphic. But I think this is closer to what you mean when you say "make D instance of Second in only that cases when it's value instance of First". Maybe it will be acceptable in your code.

like image 54
Owen Avatar answered Jan 02 '23 01:01

Owen


The exact solution will depend on what the code is trying to do, but the problem is that the type signature you give to func2 does not mention the First s constraint, whereas your definition of func2 for the Second D instance needs it. The following compiles:

class First s where
  func1 :: s -> s
class Second a where
  func2 :: First s => s -> a s

data D s = D {value :: s}
myFunc2 :: First s => s -> D s
myFunc2 = undefined

instance Second D where
  func2 = myFunc2
like image 28
Grzegorz Chrupała Avatar answered Jan 02 '23 00:01

Grzegorz Chrupała