Implicit type of numeric constants
Hello, I'm a newcomer to Haskell and I've been typing some simple expressions into Hugs in an attempt to understand how the built-in numeric types work . However, I'm puzzled by the following example: k = 2 f :: Int -> Int -> Int f x y = x * y After loading a file containing this code into Hugs, if I type "f 2 2" I get the value 4 as expected, but if I type "f k k" I get a type error because the type of k is inferred to be Integer. This seems like a violation of referential transparency to me - I would expect the inferred type of k to be the same as the type of 2. However, if I type an apparently equivalent let expression into Hugs directly, then I get the value 4 as expected let k = 2 ; f :: Int -> Int -> Int ; f x y = x * y in f k k Why is there a difference in behaviour? For that matter, if the type of 2 is "Num a => a", which I understand to mean "an arbitrary numeric type", why is it OK to pass 2 to a function that expects an Int? Thank you. Robert Stroud
Hi Robert,
I'm a newcomer to Haskell and I've been typing some simple expressions into Hugs in an attempt to understand how the built-in numeric types work . However, I'm puzzled by the following example:
k = 2
f :: Int -> Int -> Int f x y = x * y
After loading a file containing this code into Hugs, if I type "f 2 2" I get the value 4 as expected, but if I type "f k k" I get a type error because the type of k is inferred to be Integer.
This seems like a violation of referential transparency to me - I would expect the inferred type of k to be the same as the type of 2.
This is caused by a mechanism called "defaulting". See section 4.3.4 of the Haskell report: <http://haskell.org/onlinereport/decls.html#default-decls>. In this case, the type of 'k' is defaulted to 'Integer'. Basically, the problem this tries to solve is that if 'k' would have type '(Num a) => a', then the expression 'show k' is ambiguous: the compiler cannot decide what instance of 'Show' and 'Num' to use (and in this case the string 'show k' actually depends on that choice: '2 :: Double' would be shown as "2.0", '2 :: Integer' as "2". You absolutely right about this defaulting breaking referential transparency.
However, if I type an apparently equivalent let expression into Hugs directly, then I get the value 4 as expected
let k = 2 ; f :: Int -> Int -> Int ; f x y = x * y in f k k
Why is there a difference in behaviour?
Here, there is no defaulting, 'k' has the polymorphic type you expect, and the use of 'k' as an argument to the monomorphically typed 'f' chooses the right instance of 'Num'.
For that matter, if the type of 2 is "Num a => a", which I understand to mean "an arbitrary numeric type", why is it OK to pass 2 to a function that expects an Int?
Well, "an arbitrary numeric type" in the sense "something of any numeric type you may wish to ask for" (the implicit quantifier for 'a' is universal, not existential). When using 'k', additional type constraints (such as the monomorphic type of 'f') determine the choice of instance. Greetings, Arie -- Mr. Pelican Shit may be Willy. ^ /e\ ---
Dear Arie, Thank you for your answers to my questions - I'd spotted the section on ambiguous types and defaults in the language manual, but I hadn't appreciated that it might be applicable in this situation because I didn't know that show and read could be applied to (almost) all types. However, I still think there's a bit of an inconsistency here. I understand that if k had the type "Num a => a", then the expression "show k" would be ambiguous, but unless I write that expression, there's no ambiguity... So it seems to me that the type checker is being a bit too eager to prevent something that hasn't happened yet. In contrast, in the case where I write "let k = 2 ...", the type checker seems happy to resolve the polymorphic type within the context of the let expression, and does what I expect. So is the problem that the context is effectively unbounded when I load the definition from a file, and hence the type checker has to be very conservative about preventing the ambiguity? I'm afraid I'm also still not clear about why 2 doesn't default to 2::Integer in the same way that k defaults to k::Integer, but that's probably because I don't understand polymorphic types properly yet. Thanks again, Robert On 20 Sep 2006, at 13:35, Arie Peterson wrote:
Hi Robert,
I'm a newcomer to Haskell and I've been typing some simple expressions into Hugs in an attempt to understand how the built-in numeric types work . However, I'm puzzled by the following example:
k = 2
f :: Int -> Int -> Int f x y = x * y
After loading a file containing this code into Hugs, if I type "f 2 2" I get the value 4 as expected, but if I type "f k k" I get a type error because the type of k is inferred to be Integer.
This seems like a violation of referential transparency to me - I would expect the inferred type of k to be the same as the type of 2.
This is caused by a mechanism called "defaulting". See section 4.3.4 of the Haskell report: <http://haskell.org/onlinereport/decls.html#default-decls>.
In this case, the type of 'k' is defaulted to 'Integer'.
Basically, the problem this tries to solve is that if 'k' would have type '(Num a) => a', then the expression 'show k' is ambiguous: the compiler cannot decide what instance of 'Show' and 'Num' to use (and in this case the string 'show k' actually depends on that choice: '2 :: Double' would be shown as "2.0", '2 :: Integer' as "2".
You absolutely right about this defaulting breaking referential transparency.
However, if I type an apparently equivalent let expression into Hugs directly, then I get the value 4 as expected
let k = 2 ; f :: Int -> Int -> Int ; f x y = x * y in f k k
Why is there a difference in behaviour?
Here, there is no defaulting, 'k' has the polymorphic type you expect, and the use of 'k' as an argument to the monomorphically typed 'f' chooses the right instance of 'Num'.
For that matter, if the type of 2 is "Num a => a", which I understand to mean "an arbitrary numeric type", why is it OK to pass 2 to a function that expects an Int?
Well, "an arbitrary numeric type" in the sense "something of any numeric type you may wish to ask for" (the implicit quantifier for 'a' is universal, not existential). When using 'k', additional type constraints (such as the monomorphic type of 'f') determine the choice of instance.
Greetings,
Arie
--
Mr. Pelican Shit may be Willy.
^ /e\ ---
Robert Stroud wrote:
However, I still think there's a bit of an inconsistency here. I understand that if k had the type "Num a => a", then the expression "show k" would be ambiguous, but unless I write that expression, there's no ambiguity...
Actually, you can give k that type, and thanks to defaulting you can then still use show k.
So it seems to me that the type checker is being a bit too eager to prevent something that hasn't happened yet.
Here it is the monomorphism restriction (Haskell report section 4.5.5) that enforces defaulting.
In contrast, in the case where I write "let k = 2 ...", the type checker seems happy to resolve the polymorphic type within the context of the let expression, and does what I expect.
I hope my last mail explained this: defaulting for monomorphic types happens quite late.
So is the problem that the context is effectively unbounded when I load the definition from a file, and hence the type checker has to be very conservative about preventing the ambiguity?
A file is (essentially) a module. This defaulting happens "when type inference for an entire module is complete" (rule 2 of the above mentioned section), because we don't want types to depend on arbitrary other modules - think of seperate compilation.
I'm afraid I'm also still not clear about why 2 doesn't default to 2::Integer in the same way that k defaults to k::Integer,
It does, that's why show 2 works. Hope that helps Christian Sievers
Christian, Thanks for your explanation - this is beginning to make more sense to me. However, I'm not finding it easy to follow the Haskell language manual, and understand how the rules apply, so please could you confirm that the following reasoning is correct... Firstly, am I right in thinking that "k = 2" is a simple pattern binding? The syntax for a top-level declaration doesn't seem to include the possibility of declaring a variable, and there doesn't seem to be an explicit syntax for pattern. However, assuming "k = 2" is a simple pattern, then it would be a restricted declaration group (because there is no explicit type signature for k ), and so the monomorphism restriction says that "the constrained type variables in the declaration may not be generalised". I think this means that you can't leave the inferred type of k as "Num a => a", you have to pick a particular numeric type a. Then Rule 2 says that if you haven't managed to choose a particular numeric type a by the time you've done your type inference for an entire module, you have to pick a default type. So this explains why Hugs thinks that the type of k is Integer if I load the code in from a separate file (module). However, Hugs is too eager to resolve the type of k and doesn't apply rule 2 correctly - hence, if I were to attempt to use k in a way that would bind it to Int in the module, Hugs would give a type error (incorrectly) because it would have already bound k to Integer. In contrast, if I use a let clause, Hugs gets this right, and resolves the type of k appropriately. OK - so far so good I hope. What I don't understand though is why the monomorphism restriction is necessary for the case of a simple constant declaration. The language manual gives two motivations, neither of which seems to apply. The first reason is to do with preventing computations being unexpectedly repeated, but there is no computation involved here, since k is defined as a constant. The second reason is to prevent ambiguity, but the example involves a non- simple binding. So why is it necessary to give a named constant a monomorphic type, when the unnamed constant has a polymorphic type? Thanks, Robert On 20 Sep 2006, at 18:37, Christian Sievers wrote:
Robert Stroud wrote:
However, I still think there's a bit of an inconsistency here. I understand that if k had the type "Num a => a", then the expression "show k" would be ambiguous, but unless I write that expression, there's no ambiguity...
Actually, you can give k that type, and thanks to defaulting you can then still use show k.
So it seems to me that the type checker is being a bit too eager to prevent something that hasn't happened yet.
Here it is the monomorphism restriction (Haskell report section 4.5.5) that enforces defaulting.
In contrast, in the case where I write "let k = 2 ...", the type checker seems happy to resolve the polymorphic type within the context of the let expression, and does what I expect.
I hope my last mail explained this: defaulting for monomorphic types happens quite late.
So is the problem that the context is effectively unbounded when I load the definition from a file, and hence the type checker has to be very conservative about preventing the ambiguity?
A file is (essentially) a module. This defaulting happens "when type inference for an entire module is complete" (rule 2 of the above mentioned section), because we don't want types to depend on arbitrary other modules - think of seperate compilation.
I'm afraid I'm also still not clear about why 2 doesn't default to 2::Integer in the same way that k defaults to k::Integer,
It does, that's why show 2 works.
Hope that helps Christian Sievers _______________________________________________ Haskell mailing list Haskell@haskell.org http://www.haskell.org/mailman/listinfo/haskell
Robert Stroud wrote:
Thanks for your explanation - this is beginning to make more sense to me. However, I'm not finding it easy to follow the Haskell language manual, and understand how the rules apply, so please could you confirm that the following reasoning is correct...
Firstly, am I right in thinking that "k = 2" is a simple pattern binding?
Yes.
The syntax for a top-level declaration doesn't seem to include the possibility of declaring a variable, and there doesn't seem to be an explicit syntax for pattern.
I couldn't find it easily either, but eventually I found it: section 3.17.1. The rest of your reasoning seems right, too.
OK - so far so good I hope. What I don't understand though is why the monomorphism restriction is necessary for the case of a simple constant declaration. The language manual gives two motivations, neither of which seems to apply. The first reason is to do with preventing computations being unexpectedly repeated, but there is no computation involved here, since k is defined as a constant. The second reason is to prevent ambiguity, but the example involves a non- simple binding. So why is it necessary to give a named constant a monomorphic type, when the unnamed constant has a polymorphic type?
I think it would make the language irregular, and you would probably also want slightly more complex constants, and then where to draw the line? What would you think of a = 2 b = a c = -a d = a+1 e = 2^16 f = 1/7 g = product [1..10] What maybe interesting to you is that when I read the claim that the k in the let clause was polymorphic, I didn't think "no, it isn't", but rather "oh, is it?", and then tried it. And the first result of trying to explain that what getting more confused. I think you don't have to know exactly all the things I described in my yesterday's mail - I didn't know them yesterday morning either. All you have to know is that when you get such errors, you can fix them with an explicit type signature. But if you wonder what a strange language that is, continue to ask, it's a bit complex, but well defined and quite regular. (I think the haskell-cafe mailing list might be more apprpriate.) All the best Christian
Hello, I don't take my advice to go to haskell-cafe :-) The discussion continued outside the mailing list, and now I have two questions myself: 1. Why do the rules of the monomorphism restriction explicitly mention *simple* pattern bindings? Where is the difference, especially as there is a translation to simple pattern bindings? Why should p | "a"=="b" = 2 | otherwise = 3 be treated different than p = if "a"=="b" then 2 else 3 2. The gentle introduction says in section 12.3: An identifier is monomorphic if is either not overloaded, or is overloaded but is used in at most one specific overloading and is not exported. How does that relate to the report? Maybe I have to withdraw what I said about haskell being well defined. All the best Christian Sievers
Arie Peterson wrote:
However, if I type an apparently equivalent let expression into Hugs directly, then I get the value 4 as expected
let k = 2 ; f :: Int -> Int -> Int ; f x y = x * y in f k k
Why is there a difference in behaviour?
Here, there is no defaulting, 'k' has the polymorphic type you expect, and the use of 'k' as an argument to the monomorphically typed 'f' chooses the right instance of 'Num'.
Well, there is no defaulting at the stage of type checking when k is given type Num a => a, the monomorphism restriction applies and this type is not generalised to forall a . (Num a) => a, then the use of k forces the type variable a to be Int, and then there is no longer any need for defaulting. So k gets a monotype which is determined by its usage, you cannot do e.g. let k = 2 ; f :: Int -> Int -> Int ; f x y = x * y in (f k k, 1/k) whereas let k :: Num a => a; k = 2; ... is possible. Defaulting in Haskell 98 happens so late that this file k = 2 f :: Int -> Int -> Int f x y = x * y r = f k k is okay. Alas, Hugs does not comply in this respect, see http://cvs.haskell.org/Hugs/pages/users_guide/haskell98.html at the end of 5.1.3. All the best, Christian Sievers
On 20 Sep 2006, at 17:28, Christian Sievers wrote:
However, if I type an apparently equivalent let expression into Hugs directly, then I get the value 4 as expected
let k = 2 ; f :: Int -> Int -> Int ; f x y = x * y in f k k
Why is there a difference in behaviour?
Here, there is no defaulting, 'k' has the polymorphic type you expect, and the use of 'k' as an argument to the monomorphically typed 'f' chooses the right instance of 'Num'.
Well, there is no defaulting at the stage of type checking when k is given type Num a => a, the monomorphism restriction applies and this type is not generalised to forall a . (Num a) => a, then the use of k forces the type variable a to be Int, and then there is no longer any need for defaulting.
So k gets a monotype which is determined by its usage, you cannot do e.g.
let k = 2 ; f :: Int -> Int -> Int ; f x y = x * y in (f k k, 1/k)
whereas let k :: Num a => a; k = 2; ... is possible.
Thanks - that's a helpful example. But why is the following not equivalent to the untyped "k = 2" case: let f :: Int -> Int -> Int ; f x y = x * y in (f 2 2, 1/2) Does the type of 2 effectively get decided twice, once as an Int, and once as a Fractional, and is this the "repeated computation" that the monomorphism restriction is intended to prevent? Otherwise, I would have expected that it wouldn't make any difference whether I used a named 2 or an anonymous 2, but imposing the monomorphism restriction on the named 2 seems to break referential transparency. Thanks. Robert
On 21 Sep 2006, at 10:46, Robert Stroud wrote:
So k gets a monotype which is determined by its usage, you cannot do e.g.
let k = 2 ; f :: Int -> Int -> Int ; f x y = x * y in (f k k, 1/k)
whereas let k :: Num a => a; k = 2; ... is possible.
Thanks - that's a helpful example. But why is the following not equivalent to the untyped "k = 2" case:
let f :: Int -> Int -> Int ; f x y = x * y in (f 2 2, 1/2)
Does the type of 2 effectively get decided twice, once as an Int, and once as a Fractional, and is this the "repeated computation" that the monomorphism restriction is intended to prevent?
Otherwise, I would have expected that it wouldn't make any difference whether I used a named 2 or an anonymous 2, but imposing the monomorphism restriction on the named 2 seems to break referential transparency.
Aha - light begins to dawn... :-) Each 2 is a different 2, so the type of 2 can be different each time it's used, whereas there's only one k so it can only have one type. Is that right? Robert
Robert Stroud wrote:
Thanks - that's a helpful example. But why is the following not equivalent to the untyped "k = 2" case:
let f :: Int -> Int -> Int ; f x y = x * y in (f 2 2, 1/2)
Does the type of 2 effectively get decided twice, once as an Int, and once as a Fractional, and is this the "repeated computation" that the monomorphism restriction is intended to prevent?
2 has the fixed polymorphic type Num a => a, which gets resolved at each occurance. This resolving happens at compile time, so there is no repeated computation at run time involved. However, you need to somehow get both a 2::Int and a 2::Double which may be seen as a trivial case of this repeated computation.
Otherwise, I would have expected that it wouldn't make any difference whether I used a named 2 or an anonymous 2, but imposing the monomorphism restriction on the named 2 seems to break referential transparency.
Only if you expect referential transparency for implicitly typed values. All you have to do is say k :: Num a => a k = 2 and everything is fine. Bye Christian Sievers
On 20 Sep 2006, at 22:21, Ashley Yakeley wrote:
Arie Peterson wrote:
You absolutely right about this defaulting breaking referential transparency.
Do you know if it can be switched off in GHC? I know one can switch on warnings when it happens, but I don't think that's the same thing.
You can use an empty default declaration to switch off default types for a particular module, but I don't think you can do this for all modules, or at least not in standard Haskell - section 4.3.4 of the language manual says: "Only one default declaration is permitted per module, and its effect is limited to that module. If no default declaration is given in a module then it assumed to be: default (Integer, Double) The empty default declaration, default (), turns off all defaults in a module." Switching off default types may introduce error messages about top- level declarations with unbound type variables - for example Ambiguous type variable `t' in the constraint: `Num t' arising from the literal `2' at /tmp/test.hs:3:4 Possible cause: the monomorphism restriction applied to the following: k :: t (bound at /tmp/test.hs:3:0) Probable fix: give these definition(s) an explicit type signature or use -fno-monomorphism-restriction Turning off the monomorphism restriction makes the errors go away, but adding an explicit polymorphic type signature is probably better. Robert
participants (4)
-
Arie Peterson -
Ashley Yakeley -
Christian Sievers -
Robert Stroud