Prefix lenses for a type - haskell

I have the following data declared:
data Something = Something
{ self :: String
, id :: String
, description :: Maybe String
, name :: String
} deriving (Generic, Show)
instance FromJSON Something
makeLensesFor
[ ("self", "somethingSelf")
, ("id", "somethingId")
, ("description", "somethingDescription")
, ("name", "somethingName")
]
''Something
Is it possible to automate the lenses creation as the naming convention is following really simple rules.
After a peek at the source code, I can see there's a LensRules instance, called abbreviatedFields, but, sadly, using it with makeLensesWith abbreviatedFields ''Something generates no lenses at all. Am I doing something wrong?
Here's the source code: https://hackage.haskell.org/package/lens-4.14/docs/src/Control.Lens.TH.html#abbreviatedFields

Related

Safe Record field query

Is there a clean way to avoid the following boilerplate:
Given a Record data type definition....
data Value = A{ name::String } | B{ name::String } | C{}
write a function that safely returns name
getName :: Value -> Maybe String
getName A{ name=x } = Just x
getName B{ name=x } = Just x
getName C{} = Nothing
I know you can do this with Template Haskell, I am looking for a cleaner soln than that, perhaps a GHC extension or something else I've overlooked.
lens's Template Haskell helpers do the right thing when they encounter partial record fields.
{-# LANGUAGE TemplateHaskell #-}
import Control.Applicative
import Control.Lens
data T = A { _name :: String }
| B { _name :: String }
| C
makeLenses ''T
This'll generate a Traversal' called name that selects the String inside the A and B constructors and does nothing in the C case.
ghci> :i name
name :: Traversal' T String -- Defined at test.hs:11:1
So we can use the ^? operator (which is a flipped synonym for preview) from Control.Lens.Fold to pull out Maybe the name.
getName :: T -> Maybe String
getName = (^? name)
You can also make Prism's for the constructors of your datatype, and choose the first one of those which matches using <|>. This version is useful when the fields of your constructors have different names, but you do have to remember to update your extractor function when you add constructors.
makePrisms ''T
getName' :: T -> Maybe String
getName' t = t^?_A <|> t^?_B
lens is pretty useful!
Why don't you use a GADT? I do not know if you are interested in using only records. But, I fell that GADTs provide a clean solution to your problem, since you can restrict what constructors are valid by refining types.
{-# LANGUAGE GADTs #-}
module Teste where
data Value a where
A :: String -> Value String
B :: String -> Value String
C :: Value ()
name :: Value String -> String
name (A s) = s
name (B s) = s
Notice that both A and B produce Value String values while C produces Value (). When you define function
name :: Value String -> String
it specifically says that you can only pass a value that has a string in it. So, you can only pattern match on A or B values. This is useful to avoid the need of Maybe in code.

how to handle capital case in JSON?

This is a stupid question, and I have tried to understand from different tutorials. When having a JSON with capital case Haskell crashes as explained by others (https://mail.haskell.org/pipermail/beginners/2013-October/012865.html). As suggested it could be solved with deriving from deriveFromJSON. DeriveJSON requires a function input, how should I write the derive statement in the below code? I am missing something in my understanding, and would appreciate any help.
import Data.Aeson.TH
data Person = Person {
Foo :: String
, bar :: String
} deriving (Eq, Show, deriveJSON)
main = do
let b = Person "t" "x"
print b
deriveJSON and friends are Template Haskell functions which will generate instances for you. As such, you should not try to list them in the deriving clause. Instead, call them in a splice at the top level like this:
{-# LANGUAGE TemplateHaskell #-}
import Data.Aeson.TH
data Person = Person {
foo :: String,
bar :: String
} deriving (Eq, Show)
$(deriveJSON defaultOptions ''Person)
As mentioned in the mailing list, you can customize the field names by overriding the fieldLabelModifier function of the defaultOptions record, for example this will change the JSON name of foo to Foo:
$(deriveFromJSON defaultOptions {
fieldLabelModifier = let f "foo" = "Foo"
f other = other
in f
} ''Person)
Do you have any control of the instance being generated? If so, don't emit keys starting with capital letters.
If not: Just define the instance yourself instead of deriving it. It's just a couple lines of code.

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.

Using Aeson generics to construct JSON with a value as key holding another value

Toying a bit with the github gist API while trying to get down with the Aeson JSON library. I've run into a problem with the generated ToJSON instance, and I don't know exactly how to solve it.
I need to contain a value inside and the key that is associated to the value also needs to be a value and not a predefined key name. It's a bit easier to show. The desired output is,
{
"public": true,
"description": "Something..",
"files": {"This Thing.md": {"content": "Here we go!"}}
}
where the value of the filename is holding the content, but currently I get,
{
"public": true,
"description": "Something..",
"files": {"filename": "This Thing.md", "content": "Here we go!"}
}
Which isn't really what I need. The current code is,
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}
import Data.Text (Text)
import Data.Aeson
import GHC.Generics
data GistContent = GistContent
{ filename :: Text
, content :: Text
} deriving (Show, Generic)
instance ToJSON GistContent
data Gist = Gist
{ description :: Text
, public :: Bool
, files :: GistContent
} deriving (Show, Generic)
instance ToJSON Gist
Under the assumption that it is possible, how would my datastructure need to look to get the desired output?.. And if that's not possible using the generics, how'd I got about it using the ToJSON instance (I can't quite figure out the structure there either)?
Your problem stems from an incorrect schema. files can currently only contain one GistContent, which is unnecessarily limiting. Instead, you'd want to have a list of GistContents:
data Gist = Gist
{ description :: Text
, public :: Bool
, files :: [GistContent]
} deriving (Show, Generic)
Now consider another constraint on Gist: each GistContent must have a different filename. A data structure that would enforce this would be Data.HashMap.Strict.HashMap. Taking the filename out of GistContent and using the filename as a key:
data GistContent = GistContent
{ content :: Text
} deriving (Show, Generic)
data Gist = Gist
{ description :: Text
, public :: Bool
, files :: HashMap Text GistContent
} deriving (Show, Generic)
Everything works out.
Here's the manually written instance (see the documentation for the class):
instance ToJSON GistContent where
toJSON (GistContent { filename = f, content = c }) = object [f .= c]
I doubt if there would be any way to get this with your existing datatype with the automatically generated instances because all they can do is to follow the datatype using a standard scheme. Note that you can still use the generic instance for Gist because that will call the (non-generic) instance for GistContent.

Why doesn't Haskell/GHC support record name overloading

I am a Haskell newbie. I have noticed that Haskell does not support record name overloading:
-- Records.hs
data Employee = Employee
{ firstName :: String
, lastName :: String
, ssn :: String
} deriving (Show, Eq)
data Manager = Manager
{ firstName :: String
, lastName :: String
, ssn :: String
, subordinates :: [Employee]
} deriving (Show, Eq)
When I compile this I get:
[1 of 1] Compiling Main ( Records.hs, Records.o )
Records.hs:10:5:
Multiple declarations of `firstName'
Declared at: Records.hs:4:5
Records.hs:10:5
Records.hs:11:5:
Multiple declarations of `lastName'
Declared at: Records.hs:5:5
Records.hs:11:5
Records.hs:12:5:
Multiple declarations of `ssn'
Declared at: Records.hs:6:5
Records.hs:12:5
Given the "strength" of the Haskell type system, it seems like it should be easy for the compiler to determine which field to access in
emp = Employee "Joe" "Smith" "111-22-3333"
man = Manager "Mary" "Jones" "333-22-1111" [emp]
firstName man
firstName emp
Is there some issue that I am not seeing. I know that the Haskell Report does not allow this, but why not?
Historical reasons. There have been many competing designs for better record systems for Haskell -- so many in fact, that no consensus could be reached. Yet.
The current record system is not very sophisticated. It's mostly some syntactic sugar for things you could do with boilerplate if there was no record syntax.
In particular, this:
data Employee = Employee
{ firstName :: String
, lastName :: String
, ssn :: String
} deriving (Show, Eq)
generates (among other things) a function firstName :: Employee -> String.
If you also allow in the same module this type:
data Manager = Manager
{ firstName :: String
, lastName :: String
, ssn :: String
, subordinates :: [Employee]
} deriving (Show, Eq)
then what would be the type of the firstName function?
It would have to be two separate functions overloading the same name, which Haskell does not allow. Unless you imagine that this would implicitly generate a typeclass and make instances of it for everything with a field named firstName (gets messy in the general case, when the fields could have different types), then Haskell's current record system isn't going to be able to support multiple fields with the same name in the same module. Haskell doesn't even attempt to do any such thing at present.
It could, of course, be done better. But there are some tricky problems to solve, and essentially no one's come up with solutions to them that have convinced everyone that there is a most promising direction to move in yet.
One option to avoid this is to put your data types in different modules and use qualified imports. In that way you can use the same field accessors on different data records and keep you code clean and more readable.
You can create one module for the employee, for example
module Model.Employee where
data Employee = Employee
{ firstName :: String
, lastName :: String
, ssn :: String
} deriving (Show, Eq)
And one module for the Manager, for example:
module Model.Manager where
import Model.Employee (Employee)
data Manager = Manager
{ firstName :: String
, lastName :: String
, ssn :: String
, subordinates :: [Employee]
} deriving (Show, Eq)
And then wherever you want to use these two data types you can import them qualified and access them as follows:
import Model.Employee (Employee)
import qualified Model.Employee as Employee
import Model.Manager (Manager)
import qualified Model.Manager as Manager
emp = Employee "Joe" "Smith" "111-22-3333"
man = Manager "Mary" "Jones" "333-22-1111" [emp]
name1 = Manager.firstName man
name2 = Employee.firstName emp
Keep in mind that after all you are using two different data types and thus Manger.firstName is another function than Employee.firstName, even when you know that both data types represent a person and each person has a first name. But it is up to you how far you go to abstract data types, for example to create a Person data type from those "attribute collections" as well.

Resources