Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell multiparameter class (templated type class)

Tags:

haskell

I'ld like to implement a general Gaussian function for scalars and vectors. In Ada or C++ I simply would choose template for this, but in Haskell it's a little bit confusing.

I would start by defining a class that can apply Gaussian operators, like combining or calculating the probability:

class Gaussian g where
  (!*) :: g -> g -> g
  prob :: g -> a -> Float -- Here, I want a to be depending on g

data Gaussian1D = Gaussian1D Float Float
data Gaussian2D = Gaussian2D (Linear.V2 Float) Linear.V2(LinearV2 Float)

, and I'ld like to have something like:

instance Gaussian Gaussian1D where
  prob :: Gaussian1D -> Float -> Float

instance Gaussian Gaussian2D where
  prob :: Gaussian2D -> Linear.V2 Float -> Float

But I'm not able to implement this in a nice way. Would this somehow be possible with a multiparameter class without digging into the field of template-haskell, eg:

class Gaussian g a where
  (!*) :: g a -> g a -> g a
  prob :: g a -> a -> Float

? At the moment, I'm failing to realize this scenario when I do domething like:

data Gaussian1D = Gaussian1D Float Float

instance Gaussian Gaussian1D Float where

with error: Expected kind ‘* -> *’, but ‘Gaussian1D’ has kind ‘*’ (btw, I don't understand why this error occurs)

Thx

like image 213
blauerreimers Avatar asked Dec 09 '22 23:12

blauerreimers


2 Answers

This seems like a textbook use-case for Functional dependencies, a GHC-only language feature that allows multi-parameter type classes where one parameter uniquely determines the other:

{-# LANGUAGE FunctionalDependencies #-}

class Guassian g a | g -> a where
  (!*) :: g -> g -> g
  prob :: g -> a -> Float

instance Gaussian Gaussian1D Float where
  -- ...
like image 130
Aplet123 Avatar answered Dec 26 '22 04:12

Aplet123


If you write g a, for example in:

(!*) :: g a -> g a -> g a

It means that g takes a type parameter. That should for example work if Gaussian1D was defined as:

data Gaussian1D a = Gaussian1D a a

But that is not the case here. You can make a typeclass with two type parameters:

{-# LANGUAGE AllowAmbiguousTypes, MultiParamTypeClasses #-}

class Guassian g a where
  (!*) :: g -> g -> g
  prob :: g -> a -> Float

But this thus means that you can make an instance for any g, a combination. This thus means that a does not depend on g, or at least not directly. You thus can implement a Gaussian Gaussian1D Double and Gaussian Gaussian1D Float.

You can however make use of functional dependencies [wiki] where the type a is determined by the type g. This thus means that no two as can be used for the same g:

{-# LANGUAGE AllowAmbiguousTypes, FunctionalDependencies, MultiParamTypeClasses #-}

class Guassian g a | g -> a where
  (!*) :: g -> g -> g
  prob :: g -> a -> Float
like image 40
Willem Van Onsem Avatar answered Dec 26 '22 03:12

Willem Van Onsem