How to have multiple types under a generic "super type" in Haskell? - haskell

[Never mind this question - In light of the feedback, I realize it is poorly phrased and too vague. I do not delete it because the answer could serve others. Thanks!]
I am looking for a type structure that allows me to put two similar ordered data types under one generic type.
For example, let's assume I have the following ordered types:
data Type1 = Type1Level1 | Type1Level2 | Type1Level3 | Type1Level4 deriving (Eq, Ord, Show, Read, Bounded, Enum)
data Type2 = Type2Level1 | Type2Level2 | Type2Level3 deriving (Eq, Ord, Show, Read, Bounded, Enum)
In the spirit, I would like a generic overarching type like:
data GenType = Type1 | Type2
But of course that won't work.
I happen to have entities that can be related to one of those types:
data Entity = Entity {
entityId :: String,
entityType :: -- here I want one of the TypeXLevelY (anything "under GenType")
} deriving (Eq, Ord, Read, Show)
But I'd also like those entities to have specific fields depending if they are of type1 or type2 (does this mean I have to make 2 different "entity" data types?). For example, an entity of type1 should have a field "input" but not the entity of type2.
Also, type1 and type2 should be able to share ordering and several functions in common, such that something like this would make sense:
testSuccType :: GenType -> GenType -> Bool
testSuccType type1 type2 = (type2 == succ type1)
It looks like I'm needing a class here but (1) I'm not sure that's the case and (2) when I tried to use qualified type for Entity, it didn't like it (apparently this kind of qualification is not supported anymore):
data GenType a => Entity = Entity {
entityId :: String,
entityType :: a
} deriving (Eq, Ord, Read, Show)
I hope this is clear enough.
Many thanks in advance for your help.

I guess I don't fully understand the question, but perhaps I can give some hints anyway.
Probably the data type definition you are looking for is:
data GenType = GT1 Type1 | GT2 Type2
Or, somewhat idiomatically, you could pun the constructor names with the type of their (only) field:
data GenType = Type1 Type1 | Type2 Type2
Then you can implement your shared function like this:
getNextLevelGenType (GT1 t1) = GT1 (getNextLevelType1 t1)
getNextLevelGenType (GT2 t2) = GT2 (getNextLevelType2 t2)
-- OR
getNextLevelGenType (Type1 t1) = Type1 (getNextLevelType1 t1)
getNextLevelGenType (Type2 t2) = Type2 (getNextLevelType2 t2)
Sometimes it makes sense to share method names via a type class, but whether this makes sense depends a lot on details not shared in the question. If it did make sense, it might look like this:
class Leveled a where getNextLevel :: a -> a
instance Leveled Type1 where getNextLevel = succ
instance Leveled Type2 where getNextLevel = succ
instance Leveled GenType where
getNextLevel (Type1 t1) = Type1 (getNextLevel t1)
getNextLevel (Type2 t2) = Type2 (getNextLevel t2)
If you go that route, there is a generic, parameterized sum type that may be worth considering as an alternative to GenType, named Either. The main reason to avoid considering it is if GenType's constructor names can serve as human-readable documentation of their meaning. (The examples above, where the names indicate the field type and nothing more, are not good human-readable documentation.) A secondary reason would be if you actually want a sum of three or more types, since nested Eithers get unwieldy.
instance (Leveled a, Leveled b) => Leveled (Either a b) where
getNextLevel (Left a) = Left (getNextLevel a)
getNextLevel (Right b) = Right (getNextLevel b)
-- OR, with Data.Bifunctor imported,
getNextLevel = bimap getNextLevel getNextLevel

Related

The limit set of types with new data like `Tree a`

Exploring and studing type system in Haskell I've found some problems.
1) Let's consider polymorphic type as Binary Tree:
data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Show
And, for example, I want to limit my considerations only with Tree Int, Tree Bool and Tree Char. Of course, I can make a such new type:
data TreeIWant = T1 (Tree Int) | T2 (Tree Bool) | T3 (Tree Char) deriving Show
But could it possible to make new restricted type (for homogeneous trees) in more elegant (and without new tags like T1,T2,T3) way (perhaps with some advanced type extensions)?
2) Second question is about trees with heterogeneous values. I can do them with usual Haskell, i.e. I can do the new helping type, contained tagged heterogeneous values:
data HeteroValues = H1 Int | H2 Bool | H3 Char deriving Show
and then make tree with values of this type:
type TreeH = Tree HeteroValues
But could it possible to make new type (for heterogeneous trees) in more elegant (and without new tags like H1,H2,H3) way (perhaps with some advanced type extensions)?
I know about heterogeneous list, perhaps it is the same question?
For question #2, it's easy to construct a "restricted" heterogeneous type without explicit tags using a GADT and a type class:
{-# LANGUAGE GADTs #-}
data Thing where
T :: THING a => a -> Thing
class THING a
Now, declare THING instances for the the things you want to allow:
instance THING Int
instance THING Bool
instance THING Char
and you can create Things and lists (or trees) of Things:
> t1 = T 'a' -- Char is okay
> t2 = T "hello" -- but String is not
... type error ...
> tl = [T (42 :: Int), T True, T 'x']
> tt = Branch (Leaf (T 'x')) (Leaf (T False))
>
In terms of the type names in your question, you have:
type HeteroValues = Thing
type TreeH = Tree Thing
You can use the same type class with a new GADT for question #1:
data ThingTree where
TT :: THING a => Tree a -> ThingTree
and you have:
type TreeIWant = ThingTree
and you can do:
> tt1 = TT $ Branch (Leaf 'x') (Leaf 'y')
> tt2 = TT $ Branch (Leaf 'x') (Leaf False)
... type error ...
>
That's all well and good, until you try to use any of the values you've constructed. For example, if you wanted to write a function to extract a Bool from a possibly boolish Thing:
maybeBool :: Thing -> Maybe Bool
maybeBool (T x) = ...
you'd find yourself stuck here. Without a "tag" of some kind, there's no way of determining if x is a Bool, Int, or Char.
Actually, though, you do have an implicit tag available, namely the THING type class dictionary for x. So, you can write:
maybeBool :: Thing -> Maybe Bool
maybeBool (T x) = maybeBool' x
and then implement maybeBool' in your type class:
class THING a where
maybeBool' :: a -> Maybe Bool
instance THING Int where
maybeBool' _ = Nothing
instance THING Bool where
maybeBool' = Just
instance THING Char where
maybeBool' _ = Nothing
and you're golden!
Of course, if you'd used explicit tags:
data Thing = T_Int Int | T_Bool Bool | T_Char Char
then you could skip the type class and write:
maybeBool :: Thing -> Maybe Bool
maybeBool (T_Bool x) = Just x
maybeBool _ = Nothing
In the end, it turns out that the best Haskell representation of an algebraic sum of three types is just an algebraic sum of three types:
data Thing = T_Int Int | T_Bool Bool | T_Char Char
Trying to avoid the need for explicit tags will probably lead to a lot of inelegant boilerplate elsewhere.
Update: As #DanielWagner pointed out in a comment, you can use Data.Typeable in place of this boilerplate (effectively, have GHC generate a lot of boilerplate for you), so you can write:
import Data.Typeable
data Thing where
T :: THING a => a -> Thing
class Typeable a => THING a
instance THING Int
instance THING Bool
instance THING Char
maybeBool :: Thing -> Maybe Bool
maybeBool = cast
This perhaps seems "elegant" at first, but if you try this approach in real code, I think you'll regret losing the ability to pattern match on Thing constructors at usage sites (and so having to substitute chains of casts and/or comparisons of TypeReps).

I get an error when i try deriving eq. How can i fix this?

I am getting this error:
No instance for (Eq T1)
arising from the first field of TT' (typeMatrix T1')
Possible fix:
use a standalone 'deriving instance' declaration,
so you can specify the instance context yourself
When deriving the instance for (Eq TT)
|
20 | } deriving (Eq, Ord)
and I don't know why and how I can fix this ( error is the same for Ord)
Here's my code:
import Data.Matrix
data T1 = T1 { x :: Char
, y :: Int
, z :: Int
}
instance Show T1 where
show t1 = [(x t1)]
data TT = TT { myMap :: Matrix T1
, flag :: Int
} deriving (Eq, Ord)
Any idea?
In your example, a value of type TT contains a value of type T1; thus, to equate two values of type TT, you also need to know how to equate two values of type T1. But you haven't defined an Eq instance for T1! Add a deriving (Eq) clause to T1 and the error will go away. The same applies to Ord.
In general, if you have a type A, which contains a value of type B, in order to derive a typeclass on A, you need to derive that same typeclass on B. As described above, this occurs because in order to implement the methods of that typeclass on A, you need to already know how that typeclass behaves on B e.g. you can't equate A values unless you know how B values are equal (which is your example above). Another example: if you want to show values of type A as a String, you need to be able to convert B to a string.

Trouble refactoring current types(possibly GADT/Type Families related)

I've got types like this:
-- There are codes
newtype ICode = ICode { fromICode :: String }
newtype RCode = RCode { fromRCode :: String }
data DCode = DCode1 | DCode2 | DCode3
-- There are locations described by type and code.
-- Current implementation looks like this:
data Location = LocType1 ICode
| LocType2 ICode
| LocType3 RCode
| LocType4 DCode
I'd like to refactor these types to address some problems, which are present in current implementation.
It's real easy to demonstrate the properties I'm after with QuickCheck Arbitrary and Aeson's FromJSON instances and one other function.
First 3 properties are needed to generate correct test data and 4th to
implement business logic.
I'd like to be able to:
make Arbitrary instances of all code types such as
instance Arbitrary ICode where
arbitrary = ...
-- same with RCode and DCode
make Arbitrary instances of types like Location1 ICode(It clearly differs from current implementation and it's what I'm trying to fix), which describe exact combination of location type and code type. Location1 ICode can contain only a subset of ICode possible values, so I have to make sure of that.
make FromJSON instances of all possible types, something in the lines of:
instance FromJSON (Location a) where
parseJSON = ...
It's needed to deserialize some json objects depending on their values.
Some functions have to work only on one location type. It's pretty inconvenient in current implementation, because I have to use either incomplete functions or not really correct return types such as Maybe. I'd like to be able to do something like:
location1IncludedInArbitraryLocation :: LocType1 -> Location a -> Bool
location1IncludedInArbitraryLocation l = ...
I believe solution lies somewhere in the GADTs/Data Families territory, but I'm not really fluent with this kind of type-fu. If several ways to resolve this issue are possible, which one would be easier to typecheck/work with later?
3 and 4 seem incompatible. This sounds like a "fallback" mechanism: use this instance if no more specific instance is available. You can get this with OverlappingInstances, but I always seem to run into trouble with it. Worth a shot, I guess.
As for the rest of your problem, it seems like you want Location to be a GADT.
data LocType = Type1 | Type2 | Type3 | Type4
data Location :: LocType -> * where
LocType1 :: ICode -> Location Type1
LocType2 :: ICode -> Location Type2
LocType3 :: RCode -> Location Type3
LocType4 :: DCode -> Location Type4
Then you can easily do:
location1IncludedInArbitraryLocation :: Location Type1 -> Location t -> Bool
location1IncludedInArbitraryLocation (LocType1 icode) l = ...
No other cases need be defined here because no other constructor will be well-typed.
I hope this gives you enough to start playing with.
(Needs DataKinds, KindSignatures, GADTs)

Haskell Refactoring Advice

I am looking for some refactoring / Best practice advice on the following code. I would like to try to avoid the extensions while maintaining separate modules for different "versions" that are mutually exclusive. My current solution is to use a class and use existential quantification to create a common type for each of the CountrySets.
This is an easy thing for me to accomplish if I was using OOP, but I can't seam to think "functional" yet.
Thanks for your time.
Province.hs
{-# LANGUAGE ExistentialQuantification, RankNTypes #-}
module Province where
class Country a where
--some stuff that is not important
data Power =
forall a. (Show a, Eq a, Country a) => Power a |
Netural |
Water
data Unit = Unit {
power :: forall a. (Show a, Eq a, Country a) => a,
piece :: Piece
data Piece = Tank | Plane
data Province = Province {
power :: Power,
provName :: String
} deriving (Eq)
instance Show Power where
show (Power b) = "Power " ++ show b
show (Netural) = "Netural"
show (Water) = "Water"
instance Eq Power where
(==) a b = Prelude.show a == Prelude.show b
Version1.hs
import Province
data CountrySet1 =
Country11 |
Country12
deriving (Eq, Show)
instance Country CountrySet1 where
provs =
one1:one2:[]
one1 = Province (Power Country11) "Place11"
one2 = Province (Power Country12) "Place12"
Version2.hs
import Province
data CountrySet2 =
Country21 |
Country22
deriving (Eq, Show)
instance Country CountrySet2 where
provs =
two1:two2:[]
two1 = Province (Power Country11) "Place21"
two2 = Province (Power Country12) "Place22"
You don't have to put class constraints in the data types. You can instead parametrize your data type on the a variable so that you can place the constraints on the type-class instances themselves, like so:
-- Note that I added a type variable to "Power"
data Power a = Power a | Neutral | Water
instance (Show a) => Show (Power a) where ...
instance (Eq a) => Eq (Power a) where ...
... or you could do what most people do and use deriving:
data Power a = Power a | Neutral | Water deriving (Eq, Show)
This generates the exact same instances you wrote (except the Eq one will be much more efficient than what you wrote). No extensions required!
Then if you want a to be a specific type, you just say so!
-- Version1.hs
myValue1 :: Power CountrySet1
myValue1 = ...
-- Version2.hs
myValue2 :: Power CountrySet2
myValue2 = ...
These are then completely compatible and both implementations can coexist alongside each other.

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