Specifying a constraint for a class - haskell

I have three data declarations in the different modules:
data Data1 = Data1
{ id :: UUID
, field2 :: String
, field3 :: Int
}
data Data2 = Data2
{ id :: UUID
, field1112 :: String
, field4433 :: String
}
data Data3 = Data3
{ id :: UUID
, field22 :: Double
, field344 :: Int
}
And a class declaration:
class MyClass1 a where
method1 ....
How can I specify a constraint for MyClass1 so that only Data1, Data2 and Data3 can be instantiated by MyClass1 because they all have the field id?
I tried the following, but it didn't seem right to me and I got errors:
-- in the different module
class MyDataBase a where
id :: a -> UUID
-- in the module Data1.hs
instance MyDataBase Data1 where
id d1 =
-- in the module Data2.hs
-- and so on....
class (MyDataBase a) => MyClass1 a where
method1 .... = -- calling

so that only Data1, Data2 and Data3 can be instantiated by MyClass1 because they all have the field id?
What about types you don't know about that also have that field?
In general, you can accomplish things like that with Lens (regular Haskell records are IMHO unusable); if you name your fields _Data1Id and _Data2Id (customizable), you'll get a HasId class with id lens and instances for that class for Data1 and Data2. Then you can use that class as a constraint further.
Of course you don't have to use makeFields and you can write the instances yourself. Not sure why you'd do that, though.
You didn't specify what errors you got, but I bumped off UndecidableInstances. Here's how to get around "Constraint is no smaller than the instance head" error:
class A a where aOp :: a -> Int
class B a where bOp :: a -> Int
data C = forall a. B a => C a
instance A C where aOp (C a) = bOp a
This will allow you to use any type that satisifies B in a A context (but as Carsten pointed out, it's also an antipattern of sorts).

You don't want to do that. The simplest way of achieving what you want is the following:
class HasId a where
getId :: a -> UUID
class HasId a => MyClass1 a where
method1 = -- NOTE: Use "getId" instead of plain "id".
when you define your data types you simply do:
data Data1 = Data1
{ id :: UUID
, field2 :: String
, field3 :: Int
}
instance HasId Data1 where
getId = id
If you want to add a new data type to the family you simply create a new instance of the HasId class.
You don't need no extensions, nothing fancy, no libraries. Sure you can probably shorten the code or do something more fancy, but I'd prefer to use simple code that works and I understand versus code that I don't understand.

Related

How to Serialize a Type to specified database columns

I am using postgresql-simple in a haskell application, and i want to be able to serialize a data type to a row in my database that doesn't have a 1 to 1 mapping of the record fields used in the data type because i am using them in other data types. (I'm Fairly New to Haskell but I think this involves using the Identifier data constructor).
Example I have a database table Users with the following columns: user_id, email, name, password, address, phone_number
now I have a type with the following format:
data UserDetails = UserDetails {
user_id :: Int,
email :: Text,
phone_number :: Maybe (Text),
password :: Text,
name :: Maybe (Text),
address :: Maybe (Text),
} deriving (Show, Generic, FromRow)
And i can have a generic ToRow implemented for this type no problem since the record fields are the same as the column names, but I have another type that i want to generate a ToRow instance for which is:
data UserEditDetails = UserEditDetails {
ued_email :: Maybe (Email),
ued_phone_number :: Maybe (Text),
ued_address :: Maybe (Text),
ued_name :: Maybe (Text),
} deriving (Show, Generic)
how would i implement a ToRow instance of this type or more generally how can i write a ToRow instance like the following pseudo code
instance ToRow UserEditDetails where
toRow a = (columnname, ued_email a)... etc
hopefully it has a function similar to Aeson where you can easily write something like:
instance ToJSON Login where
toJSON = genericToJSON defaultOptions { fieldLabelModifier = drop 4 }
but i havent found this.
postgresql-simple doesn't pay any attention to database column names. It just expects the count and order of columns serialized or deserialized by the ToRow/FromRow instances will match the count and order of columns in the SQL query. You can use the generic instance for ToRow UserEditDetails as long as the query you're feeding it to matches the four columns in the right order. For example, if you have the following definition and generic ToRow instance:
data UserEditDetails = UserEditDetails
{ ued_email :: Maybe Text
, ued_phone_number :: Maybe Text
, ued_address :: Maybe Text
, ued_name :: Maybe Text
} deriving (Show, Generic, ToRow)
then I believe the following query will work fine:
do let q = "update user_table set email=?, phone_number=?, address=?, name=? "
++ "where user_id=?"
execute conn q $ (UserEditDetails
(Just "me#example.invalid")
(Just "555-1212")
(Just "123 Fake Street")
(Just "Bob Jones"))
:. Only (15 :: Int) -- user_id 15
Note that the generic instances from FromRow and ToRow are equivalent to:
instance ToRow UserEditDetails where
toRow (UserEditDetails a b c d) = [toField a, toField b, toField c, toField d]
instance FromRow UserEditDetails where
fromRow = UserEditDetails <$> field <*> field <*> field <*> field
The fields are anonymous and there's no place to give specific database columns anyway.

What is the right way to declare data that is an extension of another data

I am modelling a set of "things". For the most part all the things have the same characteristics.
data Thing = Thing { chOne :: Int, chTwo :: Int }
There is a small subset of things that can be considered to have an "extended" set of characteristics in addition to the base set shared by all members.
chThree :: String
I'd like to have functions that can operate on both kinds of things (these functions only care about properties chOne and chTwo):
foo :: Thing -> Int
I'd also like to have functions that operate on the kind of things with the chThree characteristic.
bar :: ThingLike -> String
I could do
data ThingBase = Thing { chOne :: Int, chTwo :: Int }
data ThingExt = Thing { chOne :: Int, chTwo :: Int, chThree :: Int }
fooBase :: ThingBase -> Int
fooExt :: ThingExt -> Int
bar :: ThingExt -> String
But this is hideous.
I guess I could use type classes, but all the boilerplate suggests this is wrong:
class ThingBaseClass a of
chOne' :: Int
chTwo' :: Int
instance ThingBaseClass ThingBase where
chOne' = chOne
chTwo' = chTwo
instance ThingBaseClass ThingExt where
chOne' = chOne
chTwo' = chTwo
class ThingExtClass a of
chThree' :: String
instance ThingExtClass ThingExt where
chThree' = chThree
foo :: ThingBaseClass a => a -> Int
bar :: ThingExtClass a => a -> String
What is the right way to do this?
One way to do so, is the equivalent of OO aggregation :
data ThingExt = ThingExt { thing :: Thing, chTree :: Int }
You can then create a class as in your post
instance ThingLike ThingExt where
chOne' = chOne . thing
chTwo' = chTwo . thing
If you are using the lens library you can use makeClassy which will generate all this boiler plate for you.
You can make a data type that is a type union of the two distinct types of things:
data ThingBase = ThingBase { chBaseOne :: Int, chBaseTwo :: Int }
data ThingExt = ThingExt { chExtOne :: Int, chExtTwo :: Int, chExtThree :: Int }
data ThingLike = CreatedWithBase ThingBase |
CreatedWithExt ThingExt
Then for any function which should take either a ThingBase or a ThingExt, and do different things depending, you can do pattern matching on the type constructor:
foo :: ThingLike -> Int
foo (CreatedWithBase (ThingBase c1 c2)) = c1 + c2
foo (CreatedWithExt (ThingExt c1 c2 c3)) = c3
-- Or another way:
bar :: ThingLike -> Int
bar (CreatedWithBase v) = (chBaseOne v) + (chBaseTwo v)
bar (CreatedWithExt v) = chExtThree v
This has the benefit that it forces you to pedantically specify exactly what happens to ThingBases or ThingExts wherever they appear to be processed as part of handling a ThingLike, by creating the extra wrapping layer of constructors (the CreatedWithBase and CreatedWithExt constructors I used, whose sole purpose is to indicate which type of thing you expect at a certain point of code).
But it has the disadvantage that it doesn't allow for overloaded names for the field accessor functions. Personally I don't see this as too big of a loss, since the extra verbosity required to reference attributes acts like a natural complexity penalty and helps motivate the programmer to keep the code sparse and use fewer bad accessor/getter/setter anti-patterns. However, if you want to go far with overloaded accessor names, you should look into lenses.
This is just one idea and it's not right for every problem. The example you already give with type classes is also perfectly fine and I don't see any good reason to call it hideous.
Just about the only "bad" thing would be wanting to somehow implicitly process ThingBases differently from ThingExts without needing anything in the type signature or the pattern matching sections of a function body to explicitly tell people reading your code precisely when and where the two different types are differentiated, which would be more like a duck typing approach which is not really what you should do in Haskell.
This seems to be what you're trying to get at by trying to force both ThingBase and ThingExt to have a value constructor with the same name of just Thing -- it seems artificially nice that the same word can construct values of either type, but my feeling is it's not actually nice. I might be misunderstanding though.
A very simple solution is to introduce a type parameter:
data ThingLike a = ThingLike { chOne, chTwo :: Int, chThree :: a }
deriving Show
Then, a ThingBase is just a ThingLike with no third element, so
type ThingBase = ThingLike ()
ThingExt contains an additional Int, so
type ThingExt = ThingLike Int
This has the advantage of using only a single constructor and only three record accessors. There is minimal duplication, and writing your desired functions is simple:
foo :: ThingLike a -> Int
foo (ThingLike x y _) = x+y
bar :: ThingExt -> String
bar (ThingLike x y z) = show $ x+y+z
One option is:
data Thing = Thing { chOne :: Int, chTwo :: Int }
| OtherThing { chOne :: Int, chTwo :: Int, chThree :: String }
Another is
data Thing = Thing { chOne :: Int, chTwo :: Int, chThree :: Maybe String }
If you want to distinguish the two Things at the type level and have overloaded accessors then you need to make use of a type class.
You could use a Maybe ThingExt field on ThingBase I guess, at least if you only have one extension type.
If you have several extensions like this, you can use a combination of embedding and matching on various constructors of the embedded data type, where each constructor represents one way to extend the base structure.
Once that becomes unmanageable, classes might become unevitable, but some kind of data type composition would still be useful to avoid duplication.

Generic type for OO classes in 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.

Inherit a data type in Haskell?

Is is possible to inherit a data type in haskell? Lets assume that:
data Data1 = Data1
{ name :: String
} deriving (Show)
On the same module, i want to do something like:
data Data2 = Data1
let x = Data2 "Something"
I wish that Data2 has the field name, 'inherited' from Data1.
I know that data Data2 = Data1 doesn't do that. So, I want to know if there is a way to do it.
thanks
Andre
Haskell doesn't have inheritance. In fact, banish all OOP related thoughts from your mind.
Instead, thinking in terms of composition is a more fruitful approach.
newtype Data1 = Data1 {name :: String}
deriving Show
newtype Data2 = Data2 {wrappedD1 :: String}
Now you could have
nameD2 :: Data2 -> String
nameD2 = name . wrappedD1
However, if you want to use the same name for both these operations, you'd want "ad-hoc polymorphism" aka overloading. In Haskell land we have typeclasses for this
data D1 = D1 {nameD1 :: String} deriving Show
data D2 = D2 {wrappedD1 :: D1} deriving Show
class Named a where
name :: a -> String
instance Named Data1 where
name = nameD1
instance Named Data2 where
name = name . wrappedD1
Now we can use name on both D1 and D2. This is similar to the concept of an interface.
Tangent: You use the toplevel syntax let foo = bar but in Haskell, we only use let bindings in GHCi due to a weird quirk of how GHCi works. Instead use just foo = bar.
Haskell tends to do well with structural subtyping and "HAS-A" relations. The most direct way to inherit Data1 is to use a newtype
newtype Data2 = Data2 { unData2 :: Data1 }
In this case Data2 is known to be identical to Data1. In fact, at compile time Data2 will be identical to Data1. The important part is that Data2 will have the chance to define entirely unique type class instances if it chooses.
Beyond that, you might make Data1 record in some more complex "inheriting" data type.
data DataMore =
DataMore { data1 :: Data1
, otherThing :: OtherThing
, somethingElse :: SomethingElse
}
Now, anything that takes a Data1 as an argument is capable of being trivially extended to take DataMores instead.
-- given...
foo :: Data1 -> Data1 -> X
-- we have
fooMore :: DataMore -> DataMore -> X
fooMore dm1 dm2 = foo (data1 dm1) (data1 dm2)
Which is a kind of contravariant subtyping.

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

Resources