Using fsharp-typeclasses to make a function that works on arbitrary monads - haskell

I'm trying to implement composM from the paper A pattern for almost compositional functions which accepts an arbitray monad and works on it. In Haskell the code would be:
data Expr =
| Var String
| Abs String Expr
composExprM :: (Monad m) => (Expr -> m Expr) -> Expr -> m Expr
composExprM f e =
case e of
Abs n e -> f e >>= (\e' -> return $ Abs n e')
_ -> return e
I tried using fsharp-typeclasses with the code below:
type Expr =
| Var of string
| Abs of string * Expr
let rec composExprM f e = match e with
| Abs (n, e) -> f e >>= (fun e' -> return' <| Abs (n, e'))
| Var _ -> return' e
But I get type inference errors:
> Could not resolve the ambiguity inherent in the use of the operator 'instance' at or near this program point. Consider using type annotations to resolve the ambiguity.
> Type constraint mismatch when applying the default type 'obj' for a type inference variable. No overloads match for method 'instance'. The available overloads are shown below (or in the Error List window). Consider adding further type constraints
> Possible overload: 'static member Return.instance : _Monad:Return * Maybe<'a> -> ('a -> Maybe<'a>)'. Type constraint mismatch. The type
obj
is not compatible with type
Maybe<'a>
The type 'obj' is not compatible with the type 'Maybe<'a>'.
> etc.....
Is what I'm trying to achieve possible with fsharp-typeclasses? Or do I have to restrict the function to only use a specific monad?

Keep in mind that polymorphic functions using this technique should be inline, that's where the "magic" resides.
So, just add the inline keyword after let rec and it will compile.
Please let me know if the function behaves as expected. I will have a look at that paper later.
In the samples you will find many functions defined to work with any Monad.
That was the main motivation when I started this project.

Related

Recursion schemes with several types

Right now, I've got an AST for expression that's polymorphic over the type of recursion:
data Expr a = Const Int
| Add a a
This has been incredibly useful by allowing me to use a type for plain recursion (Fix Expr) and another one when I need to attach extra information (Cofree Expr ann).
The issue occurs when I want to introduce another type into this recursion scheme:
data Stmt a = Compound [a]
| Print (Expr ?)
I'm not sure what to put for the Expr term without introducing additional type variables and breaking compatibility with all the general functions I've already written.
Can this be done, and if so, is it a useful pattern?
The recursion-schemes perspective is to view recursive types as fixed points of functors. The type of expressions is the fixed point of the following functor:
data ExprF expr = Const Int
| Add expr expr
The point of changing the name of the variable is to make explicit the fact that it is a placeholder for the actual type of expressions, that would otherwise be defined as:
data Expr = Const Int | Add Expr Expr
In Stmt, there are two recursive types, Expr and Stmt itself. So we put two holes/unknowns.
data StmtF expr stmt = Compound [stmt]
| Print expr
When we take a fixpoint with Fix or Cofree, we are now solving a system of two equations (one for Expr, one for Stmt), and that comes with some amount of boilerplate.
Instead of applying Fix or Cofree directly, we generalize, taking those fixpoint combinators (Fix, Cofree, Free) as parameters in the construction of expressions and statements:
type Expr_ f = f ExprF
type Stmt_ f = f (StmtF (Expr_ f))
Now we can say Expr_ Fix or Stmt_ Fix for the unannotated trees, and Expr_ (Flip Cofree ann), Stmt_ (Flip Cofree ann). Unfortunately we have to pay another LOC fee to make the kinds match, and the types get ever more convoluted.
newtype Flip cofree a f b = Flip (cofree f a b)
(This also assumes we want to use the same Fix or Cofree everywhere at the same times.)
Another representation to consider is (called HKD nowadays):
data Expr f = Const Int
| Add (f Expr) (f Expr)
data Stmt f = Compount [f Stmt]
| Print (f (Expr f))
where you only abstract from annotation/no-annotation (f = Identity or (,) ann) and not from recursion.

Getting an error: parse error on input ‘Just’

data Type = Nat | Bool | App Type Type | Var String
deriving (Eq, Show)
type Substitution = [(String, Type)]
apply :: Substitution -> Type -> Type
apply s Nat = Nat
apply s Bool = Bool
apply s Var c = case (lookup s c) of
Nothing -> (Var c)
Just v -> v
But the compilers give me the error "error: parse error on input ‘Just’
"
What am I doing wrong?
I can not reproduce the error locally, so my guess is that you used tabs an spaces, if you however copy paste your code into the editor, it should "work". In that case we however receive another error:
GHCi, version 8.0.2: http://www.haskell.org/ghc/ :? for help
[1 of 1] Compiling Main ( tmp.hs, interpreted )
tmp.hs:7:1: error:
Equations for ‘apply’ have different numbers of arguments
tmp.hs:7:1-17
tmp.hs:(9,1)-(11,29)
Failed, modules loaded: none.
This is due to the fact that you write:
apply s Var c = -- ...
and Haskell assumes that you here wrote three parameters: s, Var, and c, but the c of course belongs to the Var data constructor. We can fix this with a pair of brackets. Furthermore you call lookup in the wrong way: lookup has type lookup :: Eq a => a -> [(a, b)] -> Maybe b, so the first argument is the key (here c), and the second argument is the lookup table s. So we can fix this with:
apply :: Substitution -> Type -> Type
apply s Nat = Nat
apply s Bool = Bool
apply s (Var c) = case (lookup c s) of
Nothing -> (Var c)
Just v -> v
Note that you can get rid of the case pattern matching, and use for instance fromMaybe :: a -> Maybe a -> a instead:
import Data.Maybe(fromMaybe)
apply :: Substitution -> Type -> Type
apply s Nat = Nat
apply s Bool = Bool
apply s d#(Var c) = fromMaybe d (lookup c s)
We can furthermore group the Nat and Bool case together:
import Data.Maybe(fromMaybe)
apply :: Substitution -> Type -> Type
apply s d#(Var c) = fromMaybe d (lookup c s)
apply s t = t
This given of course that in case the Type is not a Var c pattern, we should return that Type.
Perhaps you need to call apply recursively as well, since the substitution can result into another Var (and thus you have to do extra lookups). This will however change the function semantically (!), so I am not sure if that is a requirement.
I got an error about the number of args to apply and about the types in lookup, but this code typechecks:
data Type = Nat | Bool | App Type Type | Var String
deriving (Eq, Show)
type Substitution = [(String, Type)]
apply :: Substitution -> Type -> Type
apply s Nat = Nat
apply s Bool = Bool
apply s (Var c) = case (lookup c s) of
Nothing -> (Var c)
Just v -> v
Note the parentheses around Var c and the order of lookup c s

Using a monad to implicitly check refinement type well-formedness

While implementing a refinement type system, I need to put in checks to make sure the types are well-formed. For example, a type like Num[100,0] shouldn't happen, where Num[lb,ub] is the type of numbers larger than lb and smaller than ub. I then wrote:
-- FORMATION RULES
class RefTy t
where tyOK :: t -> Bool
instance RefTy Ty
where tyOK (NumTy (n1, n2)) = n1 <= n2
tyOK (CatTy cs) = isSet cs
{-
data WellFormed t = Valid t
| Invalid
instance Monad WellFormed
where
(>>=) :: RefTy a => WellFormed a -> (a -> WellFormed b) -> WellFormed b
Valid t >>= f
| tyOK t = f t
| otherwise = Invalid
Invalid >>= _ = Invalid
-}
Which got me into the known problem of "restricted Monad". The suggested answer is to have the Wellformed monad to be general but restrict the functions. However that would go back to adding the well-formed check everywhere. Is there a better way to get around?
In your case, I don't think you actually want a monad, just the sugar that accompanies do notation. For example, have you thought about what your definition of Applicative will look like? Things get messy fast when you try to cheat your way through this.
Instead, if you want to use the do-notation, I suggest you use
{-# LANGUAGE RebindableSyntax #-}
which allows you to redefine, amongst other thing, the (>>=) and return used in desugaring a do block. You could then write something like:
myBind :: RefTy t1 => WellFormed t1 -> (t1 -> WellFormed t2) -> WellFormed t2
myBind Invalid _ = Invalid
myBind (Valid t) f | tyOK t = f t
| otherwise Invalid
myReturn :: WellFormed t
myReturn t = Valid t
I'm not sure I agree with those definitions, but regardless you would then be able to write something like
do
...
where (>>=) = myBind
return = myReturn

How do you apply function constraints in instance methods in Haskell?

I'm learning how to use typeclasses in Haskell.
Consider the following implementation of a typeclass T with a type constrained class function f.
class T t where
f :: (Eq u) => t -> u
data T_Impl = T_Impl_Bool Bool | T_Impl_Int Int | T_Impl_Float Float
instance T T_Impl where
f (T_Impl_Bool x) = x
f (T_Impl_Int x) = x
f (T_Impl_Float x) = x
When I load this into GHCI 7.10.2, I get the following error:
Couldn't match expected type ‘u’ with actual type ‘Float’
‘u’ is a rigid type variable bound by
the type signature for f :: Eq u => T_Impl -> u
at generics.hs:6:5
Relevant bindings include
f :: T_Impl -> u (bound at generics.hs:6:5)
In the expression: x
In an equation for ‘f’: f (T_Impl_Float x) = x
What am I doing/understanding wrong? It seems reasonable to me that one would want to specialize a typeclass in an instance by providing an accompaning data constructor and function implementation. The part
Couldn't match expected type 'u' with actual type 'Float'
is especially confusing. Why does u not match Float if u only has the constraint that it must qualify as an Eq type (Floats do that afaik)?
The signature
f :: (Eq u) => t -> u
means that the caller can pick t and u as wanted, with the only burden of ensuring that u is of class Eq (and t of class T -- in class methods there's an implicit T t constraint).
It does not mean that the implementation can choose any u.
So, the caller can use f in any of these ways: (with t in class T)
f :: t -> Bool
f :: t -> Char
f :: t -> Int
...
The compiler is complaining that your implementation is not general enough to cover all these cases.
Couldn't match expected type ‘u’ with actual type ‘Float’
means "You gave me a Float, but you must provide a value of the general type u (where u will be chosen by the caller)"
Chi has already pointed out why your code doesn't compile. But it's not even that typeclasses are the problem; indeed, your example has only one instance, so it might just as well be a normal function rather than a class.
Fundamentally, the problem is that you're trying to do something like
foobar :: Show x => Either Int Bool -> x
foobar (Left x) = x
foobar (Right x) = x
This won't work. It tries to make foobar return a different type depending on the value you feed it at run-time. But in Haskell, all types must be 100% determined at compile-time. So this cannot work.
There are several things you can do, however.
First of all, you can do this:
foo :: Either Int Bool -> String
foo (Left x) = show x
foo (Right x) = show x
In other words, rather than return something showable, actually show it. That means the result type is always String. It means that which version of show gets called will vary at run-time, but that's fine. Code paths can vary at run-time, it's types which cannot.
Another thing you can do is this:
toInt :: Either Int Bool -> Maybe Int
toInt (Left x) = Just x
toInt (Right x) = Nothing
toBool :: Either Int Bool -> Maybe Bool
toBool (Left x) = Nothing
toBool (Right x) = Just x
Again, that works perfectly fine.
There are other things you can do; without knowing why you want this, it's difficult to suggest others.
As a side note, you want to stop thinking about this like it's object oriented programming. It isn't. It requires a new way of thinking. In particular, don't reach for a typeclass unless you really need one. (I realise this particular example may just be a learning exercise to learn about typeclasses of course...)
It's possible to do this:
class Eq u => T t u | t -> u where
f :: t -> u
You need FlexibleContextx+FunctionalDepencencies and MultiParamTypeClasses+FlexibleInstances on call-site. Or to eliminate class and to use data types instead like Gabriel shows here

A "truly" generic function in Haskell

Suppose I have a compound data type -
data M o = M (String,o)
Now, I can define a function that works for ALL M irrespective of o. For example -
f :: M o -> M o
f (M (s,o)) = M (s++"!", o)
However, f is not really as general as I'd like it to be. In particular, using f in an expression fixes the type of o so I cannot use f again with a different type of o. For example the following does not typecheck -
p f = undefined where
m1 = M ("1", ())
m2 = M ("2", True)
m1' = f m1
m2' = f m2
It produces the error - Couldn't match expected type 'Bool' with actual type '()'
Surprisingly, if I don't provide f as an argument and instead simply use the global definition of f then it compiles and works fine! i.e. this compiles -
p = undefined where
m1 = M ("1", ())
m2 = M ("2", True)
m1' = f m1
m2' = f m2
Is there a particular reason for this? How can I work around this problem i.e. define a function f that can be applied to all (M o), even when the o varies within the same expression? I'm guessing existential types come into play here but I just can't figure out how.
The problem is that the compiler cannot infer the type of p properly. You'll have to give it a type signature:
p :: (forall o. M o -> M o) -> a
This is a rank 2 type, so you'll need a language extension:
{-# LANGUAGE Rank2Types #-}
or
{-# LANGUAGE RankNTypes #-}
Read more about it here: http://www.haskell.org/haskellwiki/Rank-N_types
Is there a particular reason for this?
Indeed, there is one. To say it in one sentence: type inference will not work properly if one lifts the restriction of the HM system that lambda arguments must be monomorphic.
Simon Peyton Jones has devised a type checker that extends HM and allows higher rank polymorphism. But in such cases, explicit type annotations are required. See Sjoerds Answer.
The f in the scope of the function p f is not the same as the top level function f. It is whatever function is given as the argument when p is called. Since you didn't give p a type signature, the compiler has to infer what f is from how you use it. Your first use implies that f :: M () -> M (), which means p :: (M () -> M ()) -> a. As someone else said, you have to give p an explicit signature using forall to do what you're trying to do.

Resources