Generic type for OO classes in Haskell - haskell

I want to have a generic type that can represent any class in a simple class diagram. In this case a class contains:
A name
Any number of arguments of any type
Any number of functions that takes any number of arguments of any type
I have only used simple ADT declarations which is not working in this case, for example this is what I have been stuck with but it gives me no where near the type of flexibility I'm after:
data Attr a = Attr { name :: String
, kind :: a}
deriving (Show)
data Action = Action { name1 :: String
, params :: [Attr Int]}
deriving (Show)
data Class a = NewC { name2 :: String
, attrs :: [Attr Int]
, actions :: [Action]}
deriving (Show)
So my question is now how would I go about representing any arbitrary class in Haskell?
I do not want to do OOP in haskell. Imaging that the class type I'm trying to make will be a node in a graph. However each node in the graph will be a different class.

I think you want to represent your class diagrams entirely as values rather than a mix of values and types. Instead of Attr Int, for example, you might use something like Attr { name="Int", kind=PrimitiveInt }. I've introduced an OopType type below.
data Attr = Attr { name :: String
, kind :: OopType}
deriving (Show)
data Action = Action { name1 :: String
, params :: [Attr]}
deriving (Show)
data Class = NewC { name2 :: String
, attrs :: [Attr]
, actions :: [Action]}
deriving (Show)
data OopType = ClassType Class
| InterfaceType Class -- TODO make a dedicated interface type
| Enum -- TODO make a dedicated enum type
| PrimitiveString
| PrimitiveInt
Note that this representation doesn't model 'generics' (that is, classes that are parameterised by types). To do that, you'd add another field to the Class type.

Related

What is the difference between single double qoute/apostrophe in template-haskell?

When learning about Haskell lenses with the Optics package, i encountered the following example:
data Person = Person
{ _name :: String
, _age :: Int
}
makeLenses ''Person
makePrisms 'Person
What does a value of type Name represent and what is the difference between that single and double single qoute/apostrophe?
Both seem to have the same type:
makeLenses, makePrisms :: Name -> DecsQ
The template-haskell documentation is incomprehensible to me. It focuses on syntax and lacks examples:
* 'f has type Name, and names the function f. Similarly 'C has type Name and names the data constructor C. In general '⟨thing⟩ interprets ⟨thing⟩ in an expression context.
* ''T has type Name, and names the type constructor T. That is, ''⟨thing⟩ interprets ⟨thing⟩ in a type context.
We have two forms of quoting to distinguish between the data constructor and the type constructor.
Consider this variant:
data Person = KPerson
{ _name :: String
, _age :: Int
}
makeLenses ''Person -- the type constructor
makePrisms 'KPerson -- the data constructor
Here it is clear that in one case we use a Name for the type constructor while in the other case we refer to a Name for the data constructor.
In principle, Haskell could have used a single form of quoting, provided that the names of constructors such as Person and KPerson are always kept distinct. Since this is not the case, we need to disambiguate between naming the type and data constructors.
Note that, in practice, it is customary to use the same name for both constructors, so this disambiguation is often needed in actual code.
Type constructors and term constructors can have the same name in Haskell, so you use double and single ticks, respectively, to indicate the difference. Here is that example from Optics with distinct names:
data Person = P
{ _name :: String
, _age :: Int
}
makeLenses ''Person
makePrisms 'P

Modeling exchange in Haskell

I'm new to Haskell, and I'm looking to model stock exchange library. It's meant to be a library, hence specifics to be defined by the user. The way I intend to use this is to have users define things like this.
data MyExchange = MyExchange { name :: ExchangeName
, base :: Currency
, quote :: Currency }
deriving (Eq, Show)
instance Exchange MyExchange
data MyExchangeBookMessage =
MyExchangeBookMessage { time :: Time
, exchange :: MyExchange
, price :: Price
, side :: Side
, amount :: Maybe Amount }
deriving (Eq, Show)
instance ExchangeBookMessage MyExchangeBookMessage
I've tried the following, but immediately ran into some limitations of type classes. Below is the code and the error message. Specifically what are the alternatives to parameterizing type classes with multiple types?
Here is the code for the library
module Lib where
data Side = Buy | Sell deriving (Eq, Show)
newtype Amount = Amount Rational deriving (Eq, Show)
newtype Price = Price Rational deriving (Eq, Show)
newtype Currency = Currency String deriving (Eq, Show)
newtype Time = Time Integer deriving (Eq, Show)
type ExchangeName = String
class Exchange a where
name :: a -> ExchangeName
base :: a -> Currency
quote :: a -> Currency
class Message a where
time :: a -> Time
class (Message a, Exchange e) => ExchangeMessage a e where
exchange :: a -> e
class ExchangeMessage a b => BookMessage a b where
price :: a -> Price
side :: a -> Side
amount :: a -> Maybe Amount
And the error message:
src/Lib.hs:22:1: error:
• Too many parameters for class ‘ExchangeMessage’
(Use MultiParamTypeClasses to allow multi-parameter classes)
• In the class declaration for ‘ExchangeMessage’
Later I would like to be able to implement type classes like this:
class Strategy s where
run (Message m, Action a) => s -> m -> a
In the Strategy implementations the run function will take an abstract message m, pattern match it against relevant Message data constructors and return specific action.
I'm porting some Scala code. in Scala I was using a hierarchy of traits with concrete case classes at the bottom:
trait Exchange {
def name: String
def base: Currency
def quote: Currency
}
case class MyExchange(base: Currency, quote: Currency) {
val name = "my-exchange"
}
trait Message {
def time: Long
}
trait ExchangeMessage extends Message {
def exchange: Exchange
}
trait BookMessage extends ExchangeMessage {
def price: Double
def side: Side
def amount: Option[Double]
}
case class MyBookMessage(time: Long, price: Double, side: Side, amount: Option[Double]) {
def exchange: Exchange = MyExchange(...)
}
First order of business, take GHC's suggestion and enable MultiParamTypeCLasses at the top of the file.
{-# LANGUAGE MultiParamTypeClasses #-}
This is a very common extension, and it will fix the immediate problem.
However there appear to be some modeling issues and if you proceed with this design you will surely hit some problems you didn't expect. I can go into all the details of what your code means, but I'm not sure that would be very helpful. Instead I'll just point you in the right direction, I think, which is to use data records instead of typeclasses. Haskell typeclasses do not correspond to classes in other OO langauges and it confuses many beginners. But I think you want to model it like this:
data Exchange = Exchange
{ name :: ExchangeName
, base :: Currency
, quote :: Currency
}
data Message = Message
{ time :: Time }
-- etc.
which will simplify everything for you, and this acts more like OO classes than your model. Keep in mind that records can have functions and other complex data structures as fields, which is how you get the analog of virtual methods, for example:
data MessageLogger = MessageLogger
{ log :: String -> IO () }
First of all, you will probably not be able to write a class instance for ExchangeMessage. The reason is that the exchange function must be able to return any type e. If you want to keep this way, you will need to provide a way to build an arbitrary exchange!
class Exchange a where
name :: a -> ExchangeName
base :: a -> Currency
quote :: a -> Currency
build :: Time -> a
This is the only possible signature for build, as all you can know from an exchange is that it has a Time you can query, and it's probably useless.
What I would think is a better design is to have concrete types for all those classes you defined. For example:
data Exchange = Exchange { getName :: ExchangeName
, getBase :: Currency
, getQuote :: Currency
} deriving (Show, Eq)
Then, once you have written the functions that work with these concrete types, you can either:
write functionsof type MyExchange -> Exchange for example, to adapt functions expecting an Exchange
use classy lenses, so as to directly write functions that will consume abitrary types
All in all, for that kind of applications, if you want to be fancy with types I would suggest to use phantom types for your currencies, so that you'll statically enforce for example you can only compute the sum of two amounts of money using the same currency. Using typeclasses to mimic the habits of OO will not produce APIs that are nice to use or code that is clear.

Implementing DataType abstraction in Haskell

I want to implement am Abstract Datype in haskell.
Given a moule with a defined type, say Mytype:
module A (myType,MyType) where
type MyType = Float
mytype :: Float -> MyType
myType f = f
Implemented internally as a single Float, I exported only a function to construct a value given a Float, and the type itself.
The problem is, when I load that module, I can acces the implementation.
Given:
module B where
import A
data OtherType = One MyType
| Two MyType MyType
deriving Show
I can construct an object of type OtherType like this:
One $ mytype 1.0
Or like this:
One $ (1.0 :: Float)
With a real abstraction I shouldn't be able to do that!
How can I export the type Mytype, in a way such that I can only construct values from my constructor functions
You can create an Algebraic Datatype instead:
module A (myType,MyType) where
data MyType = MyType Float
mytype :: Float -> MyType
myType f = MyType f
Then, trying to evaluate things like
One (MyType 3.0)
throws "Not in scope: data constructor `MyType'"

Convert Lens' a b into Lens' a (Maybe b)

I have several data structures like
data Data1 = Data1
{ _data1Field :: Int
-- More fields
} deriving (Eq, Show)
makeLenses ''Data1
data Data2 = Data2
{ _data2Field :: Int
-- More fields
} deriving (Eq, Show)
makeLenses ''Data2
-- More similar data types
So I decided to write a simple type class to make it easier to compose
class HasField a where
field :: Lens' a Int
instance HasField Data1 where
field = data1Field
instance HasField Data2 where
field = data2Field
But then I ran into the problem that some of these structures have the corresponding field as optional
data Data3 = Data3
{ _data3Field :: Maybe Int
-- More fields
} deriving (Eq, Show)
makeLenses ''Data3
And now I can no longer use the type class. Since there are about the same number of data types that have that field optional as not, I decided that it'd be better to change the typeclass:
class HasField a where
field :: Lens' a (Maybe Int)
instance HasField Data3 where
field = data3Field
But since I'm not very experienced with the lens library, I'm stuck figuring out how to make this new lens work with the types for Data1 and Data2. Ideally, I'd like to be able to view it and get a Maybe Int value for any type, and when setting I'd like Just x to set the field to x for Data1 and Data2 and be a no-op for those two types when passed Nothing.
Is this possible using existing combinators or am I going to have to write the lens myself? I'm fine doing so, but the majority of existing tutorials use TH and gloss over the details of writing one by hand.
I'm using GHC 7.6.3 and lens 3.10.
As a follow up to shachaf
class HasFieldA d where
field :: Traversal' d Int
instance HasFieldA Data1 where
field = data1Field -- Lens's are Traversals
instance HasFieldA Data3 where
field = data3Field . _Just
And then the ^? operator or the ^.. operator
getField :: HasFieldA d => d -> Maybe Int
getField = d ^? field -- or preview field d
to get it.
To set optional fields, you'd need another function
class SetFieldA d where
setField :: Setter' d Int
instance SetFieldA Data3 where
setField = set data3Field . Just

Haskell polymorphic functions with records and class types

this post is the following of this one.
I'm realizing a simple battle system as toy project, the typical system you can find in games like Final Fantasy et simila. I've solved the notorious "Namespace Pollution" problem with a class type + custom instances. For example:
type HitPoints = Integer
type ManaPoints = Integer
data Status = Sleep | Poison | .. --Omitted
data Element = Fire | ... --Omitted
class Targetable a where
name :: a -> String
level :: a -> Int
hp :: a -> HitPoints
mp :: a -> ManaPoints
status :: a -> Maybe [Status]
data Monster = Monster{monsterName :: String,
monsterLevel :: Int,
monsterHp :: HitPoints,
monsterMp :: ManaPoints,
monsterElemType :: Maybe Element,
monsterStatus :: Maybe [Status]} deriving (Eq, Read)
instance Targetable Monster where
name = monsterName
level = monsterLevel
hp = monsterHp
mp = monsterMp
status = monsterStatus
data Player = Player{playerName :: String,
playerLevel :: Int,
playerHp :: HitPoints,
playerMp :: ManaPoints,
playerStatus :: Maybe [Status]} deriving (Show, Read)
instance Targetable Player where
name = playerName
level = playerLevel
hp = playerHp
mp = playerMp
status = playerStatus
Now the problem: I have a spell type, and a spell can deal damage or inflict a status (like Poison, Sleep, Confusion, etc):
--Essentially the result of a spell cast
data SpellEffect = Damage HitPoints ManaPoints
| Inflict [Status] deriving (Show)
--Essentially a magic
data Spell = Spell{spellName :: String,
spellCost :: Integer,
spellElem :: Maybe Element,
spellEffect :: SpellEffect} deriving (Show)
--For example
fire = Spell "Fire" 20 (Just Fire) (Damage 100 0)
frogSong = Spell "Frog Song" 30 Nothing (Inflict [Frog, Sleep])
As suggested in the linked topic, I've created a generic "cast" function like this:
--cast function
cast :: (Targetable t) => Spell -> t -> t
cast s t =
case spellEffect s of
Damage hp mana -> t
Inflict statList -> t
As you can see the return type is t, here showed just for consistency. I want be able to return a new targetable (i.e. a Monster or a Player) with some field value altered (for example a new Monster with less hp, or with a new status). The problem is that i can't just to the following:
--cast function
cast :: (Targetable t) => Spell -> t -> t
cast s t =
case spellEffect s of
Damage hp' mana' -> t {hp = hp', mana = mana'}
Inflict statList -> t {status = statList}
because hp, mana and status "are not valid record selector". The problem is that I don't know a priori if t will be a monster or a player, and I don't want to specify "monsterHp" or "playerHp", I want to write a pretty generic function.
I know that Haskell Records are clumsy and not much extensibile...
Any idea?
Bye and happy coding,
Alfredo
Personally, I think hammar is on the right track with pointing out the similarities between Player and Monster. I agree you don't want to make them the same, but consider this: Take the type class you have here...
class Targetable a where
name :: a -> String
level :: a -> Int
hp :: a -> HitPoints
mp :: a -> ManaPoints
status :: a -> Maybe [Status]
...and replace it with a data type:
data Targetable = Targetable { name :: String
, level :: Int
, hp :: HitPoints
, mp :: ManaPoints
, status :: Maybe [Status]
} deriving (Eq, Read, Show)
Then factor out the common fields from Player and Monster:
data Monster = Monster { monsterTarget :: Targetable
, monsterElemType :: Maybe Element,
} deriving (Eq, Read, Show)
data Player = Player { playerTarget :: Targetable } deriving (Eq, Read, Show)
Depending on what you do with these, it might make more sense to turn it inside-out instead:
data Targetable a = Targetable { target :: a
, name :: String
-- &c...
}
...and then have Targetable Player and Targetable Monster. The advantage here is that any functions that work with either can take things of type Targetable a--just like functions that would have taken any instance of the Targetable class.
Not only is this approach nearly identical to what you have already, it's also a lot less code, and keeps the types simpler (by not having class constraints everywhere). In fact, the Targetable type above is roughly what GHC creates behind the scenes for the type class.
The biggest downside to this approach is that it makes accessing fields clumsier--either way, some things end up being two layers deep, and extending this approach to more complicated types can nest them deeper still. A lot of what makes this awkward is the fact that field accessors aren't "first class" in the language--you can't pass them around like functions, abstract over them, or anything like that. The most popular solution is to use "lenses", which another answer mentioned already. I've typically used the fclabels package for this, so that's my recommendation.
The factored-out types I suggest, combined with strategic use of lenses, should give you something that's simpler to use than the type class approach, and doesn't pollute the namespace the way having lots of record types does.
I can suggest three possible solutions.
1) Your types are very OO-like, but Haskell can also express "sum" types with parameters:
data Unit = UMon Monster | UPlay Player
cast :: Spell -> Unit -> Unit
cast s t =
case spellEffect s of
Damage hp' mana' -> case t of
UMon m -> UMon (m { monsterHp = monsterHp m - hp', monsterMana = undefined})
UPluy p -> UPlay (p { playerHp = playerHp p - hp'})
Inflict statList -> undefined
Thing that are similar in OO-design often become "sum" types with parameters in Haskell.
2) You can do what Carston suggests and add all your methods to type classes.
3) You can change your read-only methods in Targetable to be "lenses" that expose both getting and setting. See the stack overflow discussion. If your type class returned lenses then it would make your spell damage possible to apply.
Why don't you just include functions like
InflicteDamage :: a -> Int -> a
AddStatus :: a -> Status -> a
into your type-class?

Resources