aclow
aclow

Reputation: 449

Omitting Unary Constructors

I am writing some code to express "expressions" built from members of a given type:

data Expr a = Simply a | Add (Expr a, Expr a) | Mult (Expr a, Expr a) 

One can define some infix operators to make it more convenient to construct these:

(+++) :: Expr a -> Expr a -> Expr a
x +++ y = Add (x, y)

(***) :: Expr a -> Expr a -> Expr a
x *** y = Mult (x, y)

Hence one can just write

(Simply "bar") +++ ((Simply "fo") *** (Simply "o"))

to get

Add (Simply "bar",Mult (Simply "fo",Simply "o"))

Now the question is if there is a way to omit the constructor Simply as well, i.e. to consider any member of the type a implicitly as a member of Expr a and obtain the example above directly from ("bar") +++ (("fo") *** ("o")).

Upvotes: 2

Views: 183

Answers (3)

firefrorefiddle
firefrorefiddle

Reputation: 3805

You can make a typeclass of expressions like this:

class IsExpr a b | a -> b where 
      ex :: a -> Expr b

This defines a class of types a which are expressions of types b, where b is declared to be determined by a using a functional dependency.

Then you go ahead and make String into an expression of type Expr String:

instance IsExpr String String where
     ex = Simply

Furthermore, somewhat unsurprisingly, expressions are also expressions:

instance IsExpr (Expr a) a where
     ex = id

Now you can redefine your operators so that they can take any combination of things which yield expressions of the same type, i.e. any argument can be a String or an Expr String or whatever you define IsExpr instances for:

(++++) :: (IsExpr a c, IsExpr b c) => a -> b -> Expr c
x ++++ y = Add (ex x, ex y)

(****) :: (IsExpr a c, IsExpr b c) => a -> b -> Expr c
x **** y = Mult (ex x, ex y)

This does need some extensions though: TypeSynonymInstances (only if you use Strings), FlexibleInstances, MultiParamTypeClasses and FunctionalDependencies. Of course there is a complexity/benefit tradeoff, so maybe the benefit isn't worth the additional complexity.

BTW, I'd rather define the data constructors as Add (Expr a) (Expr a). You are adding an additional indirection by using a tuple.

Upvotes: 10

crockeea
crockeea

Reputation: 21811

If you insist on using mathematical operators on Strings, you can make Expr a an instance of the Num class: make a new typeclass with those operations:

{-# LANGUAGE TypeFamilies #-}

class ExprOps a where
   type Internal a
   (+++) :: a -> a -> a
   (***) :: a -> a -> a
   fromInternal :: Internal a -> a

instance ExprOps (Expr a) where
  type Internal (Expr a) = a
  x +++ y = Add (x,y)
  x *** y = Mult (x,y)
  fromInternal = Simply

Then you can simply write

foo :: Expr String
foo = "bar" +++ "fo" *** "o"

which still results in the expression

Add (Simply "bar",Mult (Simply "fo",Simply "o"))

but does not require any explicit Simply constructors.

Upvotes: 2

daniel gratzer
daniel gratzer

Reputation: 53871

You can remove the Simply constructor using the -XOverloadedStrings extension.

 {-# LANGUAGE OverloadedStrings, FlexibleInstances #-}
 import Data.String

 instance IsString (Expr String) where
   fromString = Simply

And then create a Num instance to get normal operators

 instance Num (Expr a) where
  (+) = curry Add
  (*) = curry Mult

And then you can do things like

"foo" + "bar" + "baz" :: Expr String

All this does is cause GHC to interpret "..."'s as fromString "...".

Upvotes: 3

Related Questions