Eta reduce and DuplicateRecordFields language extension - haskell

Issue is about having 2 data types, Transaction and FormatModel, both having formatId field. To prevent adding type signatures to get formatId from a transaction or formatModel, I have created type class HasFormat:
class HasFormat a where
formatId_ :: a -> FormatId
instance HasFormat Transaction where
formatId_ x = formatId x -- gives error because ambiguous occurrence ‘formatId’
instance HasFormat FormatModel where
formatId_ = formatId -- this works
Can some explain why the instance which has eta reduced implementation is working and the other one not?

Disambiguation of duplicate record fields is necessarily a best-effort kind of thing because it needs to occur before type checking (you can't generally type check an expression before you know what identifiers the names in it refer to; which is what the disambiguation is doing).
Your non-working example is equivalent to this non-working example from the documentation:
data S = MkS { x :: Int }
data T = MkT { x :: Bool }
bad :: S -> Int
bad s = x s

Related

Inferred type in data declaration

On several occasions now I have run into trouble when trying to include a monadic action of an inferred type into an existing, explicitly typed data declaration:
data Thing = Thing { x :: Int }
t = Thing { x = 42 }
-- action :: m (Maybe (n ())), for some horrendous,
-- deeply parameterized monad transformer stacks m, n
action = return Nothing
data Thing' = Thing' { x :: Int, y :: ? }
t' = Thing' { x = 42, y = action }
I don’t want to write down what the complete type of the new field is—in fact, I tried and failed. At most other times I can let the rest of the program inform the type of expressions, and I wish that I could do that here.
I’m surmising that I must give up a lot of compiler type inference support when I include data of such an inferred type in a user-defined data structure. Is that accurate, or am I missing something?
I’m surmising that I must give up a lot of compiler type inference support when I include data of such an inferred type in a user-defined data structure. Is that accurate, or am I missing something?
Correct, type inference does not go from usage places to type definitions.
You could parametrise Thing' with y's type:
data Thing' yType = Thing' { x :: Int, y :: yType }
Then compiler would infer t''s type by its usage.
This will work
{-# LANGUAGE RankNTypes, ExistentialQuantification #-}
data Thing' = forall a m. Monad m => Thing' { x:: Int, y :: m a }
But you are correct, enabling these language features will make the compiler weaker in some sense, in that type inference is no longer powerful enough to work out the type of something, you will have to add a type ascription, so that the type checker can fill in. Or at least that is how I understand it.

Add element to a list which is part of a type in Haskell

I am trying to add a new element of type Transaction as defined:
--Build type Transaction--
data Transaction = Transaction {
start_state :: Int,
symbol :: Char,
end_state :: Int
} deriving Show
to the list of transactions which is part of the following type:
--Build type Automaton--
data Automaton = Automaton {
initial_state :: Int,
states :: Set.Set Int,
transactions :: [Transaction],
final_states :: Set.Set Int
} deriving Show
I have tried to implement the following function:
--Add to the automaton a transaction
insert_transaction :: Transaction -> Automaton -> Automaton
insert_transaction t a = a{transactions = t : transactions }
but it returns the following error:
automaton.hs:25:48: error:
• Couldn't match expected type ‘[Transaction]’
with actual type ‘Automaton -> [Transaction]’
• Probable cause: ‘transactions’ is applied to too few arguments
In the second argument of ‘(:)’, namely ‘transactions’
In the ‘transactions’ field of a record
In the expression: a {transactions = t : transactions}
|
25 | insert_transaction t a = a{transactions = t : transactions }
| ^^^^^^^^^^^^
How can I implement this?
Thank you!
transactions is not really a value of type [Transaction], it's by itself† only a record field accessor to a field of type [Transaction]. I.e., it's a function. GHCi could have told you:
Main*> :t transactions
transactions :: Automaton -> Transactions
So, to access those transactions, you need to be explicit what automaton from, by just giving it as an argument:
insertTransaction :: Transaction -> Automaton -> Automaton
insertTransaction t a = a{ transactions = t : transactions a }
†“By itself” meaning: when you use it in a normal Haskell expression without special record syntax like a{transactions = ...}. There, the transactions isn't really an independent entity at all.
There are a couple of alternatives you could consider:
You can “partially pattern match” on the fields of a record you pass as a function argument.
insertTransaction t a{transactions = oldTransactions}
= a{ transactions = t : oldTransactions }
The RecordWildCards extension can bring an entire record's fields in scope as overloaded variables, somewhat like as if you're in an OO method of the class containing those fields. This allows you to write essentially what you had originally:
{-# LANGUAGE RecordWildCards #-}
insertTransaction t a{..} = a{ transactions = t : transactions }
I would not recommend this: RecordWildCards is an unidiomatic hack around the limitations of Haskell98' record types.
Lenses are the modern and consequent way to use records in Haskell. You can get them most easily by slightly changing your data definition:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data Automaton = Automaton {
_initialState :: Int
, _states :: Set.Set Int
, _transactions :: [Transaction]
, _finalStates :: Set.Set Int
} deriving (Show)
makeLenses ''Automaton -- Generates `transactions` etc. not as plain accessor
-- functions, but as “optics” which can both get
-- and set field values.
insertTransaction :: Transaction -> Automaton -> Automaton
insertTransaction t = transactions %~ (t:)

Why DuplicateRecordFields cannot have type inference?

Related post: How to disambiguate selector function?
https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/DuplicateRecordFields
However, we do not infer the type of the argument to determine the datatype, or have any way of deferring the choice to the constraint solver.
It's actually annoying that this feature is not implemented. I tried to look up multiple sources but I could not find a reason why they decide not to infer types.
Does anyone know a good reason for this? Is it because of a limitation of the current type system?
You will be interested in OverloadedRecordFields which is still being implemented.
The current implementation is intentionally crippled, so as to not introduce too much new stuff all at once. Inferring the record projection types turns out to open a nasty can of worms (which the aforementioned extension addresses).
Consider the following GHCi interaction
ghci> data Record1 = Record1 { field :: Int }
ghci> data Record2 = Record2 { field :: Bool }
ghci> :t field
What should the type of field be now? Somehow, we need a way to capture the notion of "any record with a field called field". To this end, OverloadedRecordFields introduces a new built-in type class
The new module GHC.Records defines the following:
class HasField (x :: k) r a | x r -> a where
getField :: r -> a
A HasField x r a constraint represents the fact that x is a field of
type a belonging to a record type r. The getField method gives the
record selector function.
Then, from our example above, it is as if the following instances were magically generated by GHC (in fact that is not actually what will happen, but it is a good first approximation).
instance HasField "field" Record1 Int where
getField (Record1 f) = f
instance HasField "field" Record2 Bool where
getField (Record2 f) = f
If you are interested, I recommend reading the proposal. The other feature I haven't mentioned is the IsLabel class. Once all of this is implemented (and some more for updating records) I look forward to being able to get my lenses for free (so I can stop declaring field names starting with an underscore and enabling TemplateHaskell for makeLenses).

Heterogeneous Data.Map in Haskell

Is it possible to do heterogeneous Data.Map in Haskell with GADT instead of Dynamic? I tried to model heterogeneous collection as laid out in this answer:
{-# LANGUAGE GADTs #-}
class Contract a where
toString :: a -> String
data Encapsulated where
Encapsulate :: Contract a => a -> Encapsulated
getTypedObject :: Encapsulated -> a
getTypedObject (Encapsulate x) = x
The idea being that Encapsulated could be used to store different objects of TypeClass a, and then extracted at run-time for specific type.
I get error about type of x not matching Contract a. Perhaps I need to specify some kind of class constraints to tell GHC that type of x in Encapsulate x is same as a in Contract a?
T.hs:10:34:
Couldn't match expected type ‘a’ with actual type ‘a1’
‘a1’ is a rigid type variable bound by
a pattern with constructor
Encapsulate :: forall a. Contract a => a -> Encapsulated,
in an equation for ‘getTypedObject’
at T.hs:10:17
‘a’ is a rigid type variable bound by
the type signature for getTypedObject :: Encapsulated -> a
at T.hs:9:19
Relevant bindings include
x :: a1 (bound at T.hs:10:29)
getTypedObject :: Encapsulated -> a (bound at T.hs:10:1)
In the expression: x
In an equation for ‘getTypedObject’:
getTypedObject (Encapsulate x) = x
I am trying this approach because I have JSON objects of different types, and depending on the type that is decoded at run-time over the wire, we want to retrieve appropriate type-specific builder from Map (loaded at run-time IO in main from configuration files, and passed to the function) and pass it decoded JSON data of the same type.
Dynamic library would work here. However, I am interested in finding out if there are other possible approaches such as GADTs or datafamilies.
your problem is that you push the a out again (which will not work) - what you can do is using the contract internally like this:
useEncapsulateContract :: Encapsulated -> String
useEncapsulateContract (Encapsulate x) = toString x
basically the compiler is telling you everything you need to know: inside you have a forall a. Contract a (so basically a constraint a to be a Contract)
On getTypedObject :: Encapsulated -> a you don't have this constraint - you are telling the compiler: "look this works for every a I'll demand"
To get it there you would have to parametrize Encapsulated to Encapsulated a which you obviously don't want.
The second version (the internal I gave) works because you have the constraint on the data-constructor and so you can use it there
to extent this a little:
this
getTypedObject :: Contract a => Encapsulated -> a
getTypedObject (Encapsulate x) = x
wouldn't work either as now the you would have Contract a but still it could be two different types which just share this class.
And to give hints to the compiler that both should be the same you would have to parametrize Encapsulate again ....
right now by doing this:
Encapsulate :: Contract a => a -> Encapsulated
you erase that information
#Carsten answer is obviously right, but my two cents that helped me understand that before.
When you write:
getTypedObject :: Encapsulated -> a
What you are "saying" is:
getTypedObject is a function that can take a value of the Encapsulated type and its result can be used whenever any type whatsoever is needed.
You can't obviously satisfy that, and the compiler won't allow you to try. You can only use the knowledge about the value inside of Encapsulated to bring out something meaningful based on the Contract. In other words, if the Contract wasn't there, there'd be no way for you to do anything meaningful with that value.
The concept here can be succintly described as type erasure and is also present in other languages, C++ being one I know of. Hence the value is in erasing every information about the type except the things you want to preserve via the contract they satisfy. The downside is that getting the original types back requires runtime inspection.
As a bonus, here's how a dynamic approach might work:
{-# LANGUAGE GADTs #-}
import Unsafe.Coerce
data Encapsulated where
Encapsulate :: Show a => a -> Encapsulated
getTypedObject :: Encapsulated -> a
getTypedObject (Encapsulate x) = unsafeCoerce x
printString :: String -> IO ()
printString = print
x = Encapsulate "xyz"
y = getTypedObject x
main = printString y
But it's very easy to see how that could break, right? :)

Why does Haskell not have records with structural typing?

I have heard Haskell described as having structural typing. Records are an exception to that though as I understand. For example foo cannot be called with something of type HRec2 even though HRec and HRec2 are only nominally different in their fields.
data HRec = HRec { x :: Int, y :: Bool }
data HRec2 = HRec2 { p :: Int, q :: Bool }
foo :: HRec -> Bool
Is there some explanation for rejecting extending structural typing to everything including records?
Are there statically typed languages with structural typing even for records? Is there maybe some debate on this I can read about for all statically typed languages in general?
Haskell has structured types, but not structural typing, and that's not likely to change.*
The refusal to permit nominally different but structurally similar types as interchangeable arguments is called type safety. It's a good thing. Haskell even has a newtype declaration to provide types which are only nominally different, to allow you to enforce more type safety. Type safety is an easy way to catch bugs early rather than permit them at runtime.
In addition to amindfv's good answer which includes ad hoc polymorphism via typeclasses (effectively a programmer-declared feature equivalence), there's parametric polymorphism where you allow absolutely any type, so [a] allows any type in your list and BTree a allows any type in your binary tree.
This gives three answers to "are these types interchangeable?".
No; the programmer didn't say so.
Equivalent for a specific purpose because the programmer said so.
Don't care - I can do the same thing to this collection of data because it doesn't use any property of the data itself.
There's no 4: compiler overrules programmer because they happened to use a couple of Ints and a String like in that other function.
*I said Haskell is unlikely to change to structural typing. There is some discussion to introduce some form of extensible records, but no plans to make (Int,(Int,Int)) count as the same as (Int, Int, Int) or Triple {one::Int, two::Int, three::Int} the same as Triple2 {one2::Int, two2::Int, three2::Int}.
Haskell records aren't really "less structural" than the rest of the type system. Every type is either completely specified, or "specifically vague" (i.e. defined with a typeclass).
To allow both HRec and HRec2 to f, you have a couple of options:
Algebraic types:
Here, you define HRec and HRec2 records to both be part of the HRec type:
data HRec = HRec { x :: Int, y :: Bool }
| HRec2 { p :: Int, q :: Bool }
foo :: HRec -> Bool
(alternately, and maybe more idiomatic:)
data HRecType = Type1 | Type2
data HRec = HRec { hRecType :: HRecType, x :: Int, y :: Bool }
Typeclasses
Here, you define foo as able to accept any type as input, as long as a typeclass instance has been written for that type:
data HRec = HRec { x :: Int, y :: Bool }
data HRec2 = HRec2 { p :: Int, q :: Bool }
class Flexible a where
foo :: a -> Bool
instance Flexible HRec where
foo (HRec a _) = a == 5 -- or whatever
instance Flexible HRec2 where
foo (HRec2 a _) = a == 5
Using typeclasses allows you to go further than regular structural typing -- you can accept types that have the necessary information embedded in them, even if the types don't superficially look similar, e.g.:
data Foo = Foo { a :: String, b :: Float }
data Bar = Bar { c :: String, d :: Integer }
class Thing a where
doAThing :: a -> Bool
instance Thing Foo where
doAThing (Foo x y) = (x == "hi") && (y == 0)
instance Thing Bar where
doAThing (Bar x y) = (x == "hi") && ((fromInteger y) == 0)
We can run fromInteger (or any arbitrary function) to get the data we need from what we have!
I'm aware of two library implementations of structurally typed records in Haskell:
HList is older, and is explained in an excellent paper: Haskell's overlooked object system (free online, but SO won't let me include more links)
vinyl is newer, and uses fancy new GHC features. There's at least one library, vinyl-gl, using it.
I cannot answer the language-design part of your question, though.
To answer your last question, Go and Scalas definitely have structural typing. Some people (including me) would call that "statically unsafe typing", since it implicitly declares all samely- named methods in a program to have the same semantics, which implies "spooky action at a distance", relating code in on source file to code in some library that the program has never seen.
IMO, better to require that the same-named methods to explicitly declare that they conform to a named semantic "model" of behavior.
Yes, the compiler would guarantee that the method is callable, but it isn't much safer than saying:
f :: [a] -> Int
And letting the compiler choose an arbitrary implementation that may or may not be length.
(A similar idea can be made safe with Scala "implicits" or Haskell (GHC?) "reflection" package.)

Resources