Record update in constructors is statically checked:
type myData = MyData1 of { name : string } | MyData2 of { value : int }
let x = MyData1 { name = "hello" }
(*
* rejected:
* let y = MyData2 { x with value = 0 }
*)
let y = match x with
| MyData1 z -> failwith "todo"
| MyData2 z -> MyData2 { z with value = 0 }
And one can write: match x with
| A | B -> f1
| C -> f2One way to deal with this is to not use fields at all together with algebraic data types (i.e. when you have more than one constructor), just pattern match. When you have some common field across all constructors, define a regular function to pull it out:
someData :: MyData -> a
and use that instead. As to the second use case, if you need to access something common to A and B but not C, then: someOtherData :: MyData -> Maybe a
and pattern match on that. Or first pattern match on C and then have a catch all _ for A and B.lens remedies a lot of Haskell's pain points when it comes to records, but I have to say that PureScripts record system is so much nicer.
Do you mean Template Haskell? If so, I'm pretty sure it's not required for use, just to have the lenses defined for you. You can still define them manually, at the cost of a little extra boilerplate.
If you actually mean ghci, then I have no idea what you're talking about...
EDIT: And this link ties Template Haskell to GHCi: http://stackoverflow.com/a/10929803
Maybe what you're thinking of is the problem with cross-compiling and Template Haskell. Generally speaking, that does not work because Template Haskell can do evil platform-specific things at compile time, so if the host and target architectures don't match, you can run into problems.
Anyway, you can use Template Haskell on ARM, you just have to compile it on ARM.
For what it's worth, lenses can avoid some of these issues. For instance, they can allow you to have the same field name in multiple records:
data RecordA { _a_name :: String }
data RecordB { _b_name :: String }
makeLensesWith underscoreFields ''RecordA
makeLensesWith underscoreFields ''RecordB
You can now use something like `view name x` or `x^.name` for values of type `RecordA` or `RecordB`. Within the record definitions themselves, the field names still have to be disambiguated. The naming convention I chose here is pretty arbitrary. The first argument to `makeLensesWith` tells it how to parse the field names to get the "true" name.https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedReco... https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedReco...
I agree that this is poor design.
Field accessors for a field present in one constructor and absent in others should give a Maybe.
I'm not certain what should be done for the case of different types. Packaging it up in an Either seems possible, but the obvious approach would make the shape of the return type depend on the order in which I've listed my constructors, which is ugly... Refusing to generate the accessor function seems okay. Disallowing the collision - effectively the current state - isn't so bad.
In practice, the rule others have mentioned works fine: pretend Haskell doesn't support named fields in types with more than one constructor. The RecordWildCards extension also makes traditional Haskell records more pleasant to work with.
A{} or B{} -> f1
a big part of the problem is that A and B are going to be putting different things into scope in the context of f1.If you're not binding anything, you could do it with guards:
...
| isA myData || isB myData -> f1
...I rarely use wildcards that aren't embedded in the middle of some larger structure. They stop the compiler from telling me points I need to consider, as I change my code.
https://downloads.haskell.org/~ghc/master/users-guide/8.0.1-...
{-# LANGUAGE DuplicateRecordFields #-}
data A = B { x :: Bool }
| C { x :: Int }
Example.hs:2:1: error:
• Constructors B and C give different types for field ‘x’
• In the data type declaration for ‘A’
My biggest gripe is record handling. There are a few libraries that help dealing with the pain of records, but if you're using 'vanilla' Haskell, they really can be frustrating.
For instance, any field name in a record pollutes the global namespace for that module.
...Which leads me to the other problem of different record constructors of the same data type having different fields, which can lead to some very ugly runtime exceptions (so much for a strong type system!): Another big issue I've recently had with my code was dealing with multiple constructors into pattern matches/pattern guard conditionals. For example, I have a record type like this: data and I want a case to match either A or B, otherwise do C. Let's say I have a function f1 that I want to run on A and B, and a function f2 that I want to run on C. I'm forced to do something like Whereas I'd love to be able to do This becomes a big problem when I have a lot of cases to take care of, and repetition makes it harder to spot bugs and easier to mess up.