beginner's type error

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Ivan Moore
Hi all,

consider this very small function:

thing n = n + round(sqrt n)

It loads into ghci with no warnings. When I try to run "thing 10" I get:

*Main> :load c:\temp\statictype.hs
[1 of 1] Compiling Main             ( C:\temp\statictype.hs, interpreted )
Ok, modules loaded: Main.
*Main> thing 10

<interactive>:1:0:
    Ambiguous type variable `t' in the constraints:
      `Integral t' arising from a use of `thing' at <interactive>:1:0-7
      `RealFrac t' arising from a use of `thing' at <interactive>:1:0-7
      `Floating t' arising from a use of `thing' at <interactive>:1:0-7
    Probable fix: add a type signature that fixes these type variable(s)

I have tried to add various type signatures (without really knowing
what I'm doing!) and haven't been able to get it to work.

I am confused about a few things related to this:
(a) what type signature fixes it and why it needs any help - it looks
like the sort of thing that type inference shouldn't need any help
with
(b) it looks like a runtime type error and I thought you didn't get
runtime type errors in Haskell
(c) if I substitute 10 for n and do "10 + round(sqrt 10)" I get the
expected answer 13

any help most welcome.

cheers,

Ivan
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Peter Verswyvelen-2
round returns an Integral type, but sqrt expects a Floating type
Prelude> :t sqrt
sqrt :: (Floating a) => a -> a
Prelude> :t round
round :: (RealFrac a, Integral b) => a -> b
Prelude>

Haskell's numeric type classes can be intimidating for beginners, but it
basically means you are combining floating point numbers with integer
numbers, and you must convert these numbers to the same type (just as in C#
or other languages, so that are aware of possible unwanted numerical
effects).

You can use functions like fromIntegral and realToFrac to convert numbers.

So try this

thing n = n + fromIntegral ( round(sqrt n) )

You can also get rid of the parentheses like this:

thing n = n + fromIntegral $ round $ sqrt n


On Thu, Mar 26, 2009 at 11:01 PM, Ivan Moore <[hidden email]> wrote:

> Hi all,
>
> consider this very small function:
>
> thing n = n + round(sqrt n)
>
> It loads into ghci with no warnings. When I try to run "thing 10" I get:
>
> *Main> :load c:\temp\statictype.hs
> [1 of 1] Compiling Main             ( C:\temp\statictype.hs, interpreted )
> Ok, modules loaded: Main.
> *Main> thing 10
>
> <interactive>:1:0:
>    Ambiguous type variable `t' in the constraints:
>      `Integral t' arising from a use of `thing' at <interactive>:1:0-7
>      `RealFrac t' arising from a use of `thing' at <interactive>:1:0-7
>      `Floating t' arising from a use of `thing' at <interactive>:1:0-7
>    Probable fix: add a type signature that fixes these type variable(s)
>
> I have tried to add various type signatures (without really knowing
> what I'm doing!) and haven't been able to get it to work.
>
> I am confused about a few things related to this:
> (a) what type signature fixes it and why it needs any help - it looks
> like the sort of thing that type inference shouldn't need any help
> with
> (b) it looks like a runtime type error and I thought you didn't get
> runtime type errors in Haskell
> (c) if I substitute 10 for n and do "10 + round(sqrt 10)" I get the
> expected answer 13
>
> any help most welcome.
>
> cheers,
>
> Ivan
> _______________________________________________
> Beginners mailing list
> [hidden email]
> http://www.haskell.org/mailman/listinfo/beginners
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090327/b9b9f8aa/attachment.htm
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Michael Mossey


Peter Verswyvelen wrote:
>
> You can also get rid of the parentheses like this:
>
> thing n = n + fromIntegral $ round $ sqrt n

I'm having a hard time finding an explanation of the dollar signs. What
do they do? It looks like they break up the left-ro-right association of
function names to arguments.

As a beginner, I love how Haskell is filled with so many good ideas, in
many areas. The basic concept of functional programming is good, but
also Haskell has beautiful syntax that's just pleasing to look at, and
also has many convenient features which may not quite qualify as
"beautiful" or "elegant" but are just convenient (still a worthy thing).

Languages that borrow from Haskell, like Python's list comprehensions,
invariably are much dumbed-down implementations. In Python, list
comprehensions don't have guards or pattern matching. (Technically you
can put in a guard via an if statement, but you are doing a lot more
typing at that point.)

Thanks,
Mike
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Brent Yorgey-2
In reply to this post by Ivan Moore
On Thu, Mar 26, 2009 at 10:01:07PM +0000, Ivan Moore wrote:
> Hi all,
>
> consider this very small function:
>
> thing n = n + round(sqrt n)

Here's what's going on: since you call sqrt on n, it must have some
type which is an instance of Floating.  However, round can return any
type which is an instance of Integral, and since you are adding n to
it, n must have the same type.

This is the takeaway point here: sqrt requires some floating-point
type (like Float or Double), but round returns an Integral type (like
Int or Integer) and n can't be both.  In particular you can't call
sqrt on an Integral value.  So the fix is to use fromIntegral to
convert:

  thing n = n + round (sqrt (fromIntegral n))

>
> It loads into ghci with no warnings.

The reason (which is a bit confusing) is that it typechecks just
fine---if there *were* a type which is an instance of both Integral
and Floating (and I guess round needs RealFrac as well), n could have
that type.  There isn't such a type in the standard libraries, but in
theory you could make up your own type which is an instance of both.

> When I try to run "thing 10" I get:
>
> *Main> :load c:\temp\statictype.hs
> [1 of 1] Compiling Main             ( C:\temp\statictype.hs, interpreted )
> Ok, modules loaded: Main.
> *Main> thing 10
>
> <interactive>:1:0:
>     Ambiguous type variable `t' in the constraints:
>       `Integral t' arising from a use of `thing' at <interactive>:1:0-7
>       `RealFrac t' arising from a use of `thing' at <interactive>:1:0-7
>       `Floating t' arising from a use of `thing' at <interactive>:1:0-7
>     Probable fix: add a type signature that fixes these type variable(s)
>
> I have tried to add various type signatures (without really knowing
> what I'm doing!) and haven't been able to get it to work.
>
> I am confused about a few things related to this:
> (a) what type signature fixes it and why it needs any help - it looks
> like the sort of thing that type inference shouldn't need any help
> with

The error message is particularly unhelpful here.  Adding a type
signature would only help if you actually had some type which was both
Integral and Floating, but you don't.

> (b) it looks like a runtime type error and I thought you didn't get
> runtime type errors in Haskell

You don't.  This isn't a runtime type error; the error was generated
while trying to typecheck the expression 'thing 10' before evaluating
it.

> (c) if I substitute 10 for n and do "10 + round(sqrt 10)" I get the
> expected answer 13

This is because numeric literals (like 10) are polymorphic---they can
have any numeric type.  In this case, type inference correctly figures
out that the first 10 should have type Integer, and the second 10
should have type Double.  The difference is that they are not
constrained to have the same type---unlike the two occurrences of 'n'
in your original function.

Confusing, isn't it!  It's a shame that numeric types can be so
confusing, since that's usually one of the first things that people
run into when learning the language.  But I hope this is helpful.
Feel free to ask if you have more questions.

-Brent
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Thomas Davie
In reply to this post by Michael Mossey

On 27 Mar 2009, at 14:01, Michael Mossey wrote:

>
>
> Peter Verswyvelen wrote:
>> You can also get rid of the parentheses like this:
>> thing n = n + fromIntegral $ round $ sqrt n
>
> I'm having a hard time finding an explanation of the dollar signs.  
> What do they do? It looks like they break up the left-ro-right  
> association of function names to arguments.

You're pretty much there!

The $ function is simply "apply":
f $ a = f a

The difference is that this version of application (as opposed to the  
version written as ' ') has very very low precidence, and can be used  
to essentially mean "apply this to the whole expression on my right".

Of note though, using chains of ($)s as peter did is commonly  
considered bad style, instead, one should use (.) to build up a  
function, and then apply it, so not:
fromIntegral $ round $ sqrt n
But instead:
fromIntegral . round . sqrt $ n

Why?  Because the latter one has more valid expressions and is  
therefor easier to refactor.  For example, in the latter one I may  
deside that (round . sqrt) is a useful function in itself (lets call  
it integralSqrt) and refactor:

fromIntegral . integralSqrt $ n
integralSqrt = round . sqrt

With this style, this is simply a matter of copy/paste.

> As a beginner, I love how Haskell is filled with so many good ideas,  
> in many areas. The basic concept of functional programming is good,  
> but also Haskell has beautiful syntax that's just pleasing to look  
> at, and also has many convenient features which may not quite  
> qualify as "beautiful" or "elegant" but are just convenient (still a  
> worthy thing).

I'm not sure, most of the convenient things I use in Haskell are also  
beautiful and elegant, did you have something in mind?

Thanks

Bob
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Brent Yorgey-2
In reply to this post by Michael Mossey
On Fri, Mar 27, 2009 at 06:01:15AM -0700, Michael Mossey wrote:
>
>
> Peter Verswyvelen wrote:
>> You can also get rid of the parentheses like this:
>> thing n = n + fromIntegral $ round $ sqrt n
>
> I'm having a hard time finding an explanation of the dollar signs. What do
> they do? It looks like they break up the left-ro-right association of
> function names to arguments.

($) is just function application.  It is defined as:

  f $ x = f x

This looks useless, of course, but it also has very low precedence, so
it is often used to avoid parentheses.  For example,

  (foo bar) (baz t)

can't be written without the parentheses, since that would be parsed
as ((foo bar) baz) t, but it can be written as

  foo bar $ baz t


> As a beginner, I love how Haskell is filled with so many good ideas, in
> many areas. The basic concept of functional programming is good, but also
> Haskell has beautiful syntax that's just pleasing to look at, and also has
> many convenient features which may not quite qualify as "beautiful" or
> "elegant" but are just convenient (still a worthy thing).

As a non-beginner, I love this too.  So I think you're on to something. =)

-Brent
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Brent Yorgey-2
In reply to this post by Peter Verswyvelen-2
>
> thing n = n + fromIntegral (round (sqrt n))
>
> thing n = n + round (sqrt (fromIntegral n))

Pop quiz for beginners: both of these solve the original problem, but
they are not quite the same.  What is the difference?  (Do not answer
this question if you are not a beginner!)

-Brent
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Edward Z. Yang
On Fri, 27 Mar 2009, Brent Yorgey wrote:
>> thing n = n + fromIntegral (round (sqrt n))

thing :: Floating a => a -> a

>> thing n = n + round (sqrt (fromIntegral n))

thing :: Integral a => a -> a

That is, the return types of the method are different?

> Pop quiz for beginners: both of these solve the original problem, but
> they are not quite the same.  What is the difference?  (Do not answer
> this question if you are not a beginner!)
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Brent Yorgey-2
On Fri, Mar 27, 2009 at 11:28:54AM -0400, Edward Z. Yang wrote:

> On Fri, 27 Mar 2009, Brent Yorgey wrote:
>>> thing n = n + fromIntegral (round (sqrt n))
>
> thing :: Floating a => a -> a
>
>>> thing n = n + round (sqrt (fromIntegral n))
>
> thing :: Integral a => a -> a
>
> That is, the return types of the method are different?

Right!

-Brent
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Ivan Moore
In reply to this post by Brent Yorgey-2
Many thanks for the fantastic answers.

I have a question related to your answer

> The reason (which is a bit confusing) is that it typechecks just
> fine---if there *were* a type which is an instance of both Integral
> and Floating (and I guess round needs RealFrac as well), n could have
> that type.  There isn't such a type in the standard libraries, but in
> theory you could make up your own type which is an instance of both.

If there were such a type, could "10" have that type and then would my
problem have not existed? (in which case, why doesn't it!?)
(an answer of - "ask again when you've used the language a bit more"
would be perfectly fine if it requires a lot
more understanding of the language to understand the answer than a
newbie like me has - I'm just curious)

On Fri, Mar 27, 2009 at 1:07 PM, Brent Yorgey <[hidden email]> wrote:

> On Thu, Mar 26, 2009 at 10:01:07PM +0000, Ivan Moore wrote:
>> Hi all,
>>
>> consider this very small function:
>>
>> thing n = n + round(sqrt n)
>
> Here's what's going on: since you call sqrt on n, it must have some
> type which is an instance of Floating. ?However, round can return any
> type which is an instance of Integral, and since you are adding n to
> it, n must have the same type.
>
> This is the takeaway point here: sqrt requires some floating-point
> type (like Float or Double), but round returns an Integral type (like
> Int or Integer) and n can't be both. ?In particular you can't call
> sqrt on an Integral value. ?So the fix is to use fromIntegral to
> convert:
>
> ?thing n = n + round (sqrt (fromIntegral n))
>
>>
>> It loads into ghci with no warnings.
>
> The reason (which is a bit confusing) is that it typechecks just
> fine---if there *were* a type which is an instance of both Integral
> and Floating (and I guess round needs RealFrac as well), n could have
> that type. ?There isn't such a type in the standard libraries, but in
> theory you could make up your own type which is an instance of both.
>
>> When I try to run "thing 10" I get:
>>
>> *Main> :load c:\temp\statictype.hs
>> [1 of 1] Compiling Main ? ? ? ? ? ? ( C:\temp\statictype.hs, interpreted )
>> Ok, modules loaded: Main.
>> *Main> thing 10
>>
>> <interactive>:1:0:
>> ? ? Ambiguous type variable `t' in the constraints:
>> ? ? ? `Integral t' arising from a use of `thing' at <interactive>:1:0-7
>> ? ? ? `RealFrac t' arising from a use of `thing' at <interactive>:1:0-7
>> ? ? ? `Floating t' arising from a use of `thing' at <interactive>:1:0-7
>> ? ? Probable fix: add a type signature that fixes these type variable(s)
>>
>> I have tried to add various type signatures (without really knowing
>> what I'm doing!) and haven't been able to get it to work.
>>
>> I am confused about a few things related to this:
>> (a) what type signature fixes it and why it needs any help - it looks
>> like the sort of thing that type inference shouldn't need any help
>> with
>
> The error message is particularly unhelpful here. ?Adding a type
> signature would only help if you actually had some type which was both
> Integral and Floating, but you don't.
>
>> (b) it looks like a runtime type error and I thought you didn't get
>> runtime type errors in Haskell
>
> You don't. ?This isn't a runtime type error; the error was generated
> while trying to typecheck the expression 'thing 10' before evaluating
> it.
>
>> (c) if I substitute 10 for n and do "10 + round(sqrt 10)" I get the
>> expected answer 13
>
> This is because numeric literals (like 10) are polymorphic---they can
> have any numeric type. ?In this case, type inference correctly figures
> out that the first 10 should have type Integer, and the second 10
> should have type Double. ?The difference is that they are not
> constrained to have the same type---unlike the two occurrences of 'n'
> in your original function.
>
> Confusing, isn't it! ?It's a shame that numeric types can be so
> confusing, since that's usually one of the first things that people
> run into when learning the language. ?But I hope this is helpful.
> Feel free to ask if you have more questions.
>
> -Brent
> _______________________________________________
> Beginners mailing list
> [hidden email]
> http://www.haskell.org/mailman/listinfo/beginners
>
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Brent Yorgey-2
On Fri, Mar 27, 2009 at 07:44:05PM +0000, Ivan Moore wrote:

> Many thanks for the fantastic answers.
>
> I have a question related to your answer
>
> > The reason (which is a bit confusing) is that it typechecks just
> > fine---if there *were* a type which is an instance of both Integral
> > and Floating (and I guess round needs RealFrac as well), n could have
> > that type.  There isn't such a type in the standard libraries, but in
> > theory you could make up your own type which is an instance of both.
>
> If there were such a type, could "10" have that type and then would my
> problem have not existed? (in which case, why doesn't it!?)
> (an answer of - "ask again when you've used the language a bit more"
> would be perfectly fine if it requires a lot
> more understanding of the language to understand the answer than a
> newbie like me has - I'm just curious)
>

If there were such a type, and if it were one of the types to which
ghci was allowed to default, then you would not have had a problem.
But if you look at the methods of the Integral and Floating classes,
you will see that such a type would likely be silly.  You can't really
have a type which is simultaneously Integral (i.e. whole numbers, with
no fractional part) and Floating (i.e. floating point numbers which
can be divided, square rooted, etc.).  To have a type be an instance
of both would require a radical reinterpretation of what these type
classes mean.

-Brent
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Jason Dusek
In reply to this post by Ivan Moore
2009/03/27 Ivan Moore <[hidden email]>:
> > The reason (which is a bit confusing) is that it typechecks
> > just fine---if there *were* a type which is an instance of
> > both Integral and Floating (and I guess round needs RealFrac
> > as well), n could have that type. ?There isn't such a type
> > in the standard libraries, but in theory you could make up
> > your own type which is an instance of both.
>
> If there were such a type, could "10" have that type and then
> would my problem have not existed?

  Yes, that is correct.

> in which case, why doesn't it!?

  Does it actually make any logical sense for a type to be both
  integral (governed by rules of modular division) and floating
  point (governed by rules of IEEE 754 division)?

--
Jason Dusek
Reply | Threaded
Open this post in threaded view
|

beginner's type error

Ivan Moore
without engaging my brain too much, given what you've said, then why
is "10" both integral and floating point? why not "10" being integral
and "10.0" being floating point?

On Tue, Mar 31, 2009 at 2:09 AM, Jason Dusek <[hidden email]> wrote:

> 2009/03/27 Ivan Moore <[hidden email]>:
>> > The reason (which is a bit confusing) is that it typechecks
>> > just fine---if there *were* a type which is an instance of
>> > both Integral and Floating (and I guess round needs RealFrac
>> > as well), n could have that type.  There isn't such a type
>> > in the standard libraries, but in theory you could make up
>> > your own type which is an instance of both.
>>
>> If there were such a type, could "10" have that type and then
>> would my problem have not existed?
>
>  Yes, that is correct.
>
>> in which case, why doesn't it!?
>
>  Does it actually make any logical sense for a type to be both
>  integral (governed by rules of modular division) and floating
>  point (governed by rules of IEEE 754 division)?
>
> --
> Jason Dusek
>
Reply | Threaded
Open this post in threaded view
|

beginner's type error

John Dorsey-2
Ivan,

> without engaging my brain too much, given what you've said, then why
> is "10" both integral and floating point? why not "10" being integral
> and "10.0" being floating point?

The literal "10", as it appears in Haskell source, stands for something
that can take on any numeric type, but as soon as you use it in a context
that constrains it, well, it gets constrained!

"10" can be integral, or it can be floating point.  As I think Jason was
saying, *if* there was a type that was both integral and floating point,
then 10 could represent something of that type.  But there's no *sensible*
type that has both integral and floating point nature.  (At least none that
comes to mind.  Maybe you could do something with symbolic manipulation
that would make sense.)

Leaving behind sensible types, we can define one that's both integral
and fractional.  I'm going to violate all kinds of commonly assumed (but
unenforced) laws of the numeric classes, in an act of self-loathing, so
enjoy the following abomination!  The code is appended at the end of
this message.

dorsey@elwood:~/src/scratch$ ghci int-float.hs
*Main> :t munge
munge :: IntFloat -> IntFloat
*Main> :t munge 10
munge 10 :: IntFloat
*Main> fromIntegral (munge 10)
42

If you look at the code for munge below, you'll see that I've mixed
integral operations (div) with floating point operations (**) and
fractional operations (/).

I have no trouble applying munge to 10.  The literal "10" (which really means
"fromIntegral 10") takes on the right type because it has to to match munge's
argument type.

So why would I choose to do this terrible thing?  To illustrate that:

1) The open world assumption of Haskell type classes implies that
   a type like this could be definied later, even if it doesn't exist now.

2) Even though you can do this and obey the type rules, I had to use
   a silly type, with very silly class instance definitions.  Integral
   things just aren't fractional!

To answer you other specific question, about why they didn't just
distinguish "10" from "10.0" as some other languages do, the original
motivation was well before my time.  But it does seem to me that being
able to use "10" to refer not only to Integers and Floats, but also to
Ints, Int16s, Doubles, and many unforseen numeric types, was a clever
choice.  Sadly, numeric literals make the short-list of things that
confuse Haskell neophites the most.

I hope all this is at least either interesting or helpful.

Regards,
John


-- int-float.hs

-- a datatype inhabiting floating and integral classes,
-- but which doesn't model numerics particularly well

data IntFloat = IF String
  deriving (Show, Eq, Ord)

munge :: IntFloat -> IntFloat
munge x = x / x `div` x ** x

instance Num IntFloat where
  _ + _ = IF "sum"
  _ - _ = IF "difference"
  _ * _ = IF "product"
  negate _ = IF "negation"
  abs _ = IF "abs"
  signum _ = IF "signum"
  fromInteger _ = IF "fromInteger"

instance Integral IntFloat where
  quot _ _ = IF "quot"
  rem  _ _ = IF "rem"
  div  _ _ = IF "div"
  mod  _ _ = IF "mod"
  quotRem _ _ = (IF "quotRem quot", IF "quotRem rem")
  divMod  _ _ = (IF "divMod div", IF "divMod mod")
  toInteger _ = 42

instance Floating IntFloat where
  pi = IF "pi"
  exp _ = IF "exp"
  sqrt _ = IF "sqrt"
  log _ = IF "log"
  (**) _ _ = IF "**"
  logBase _ _ = IF "logBase"
  sin _ = IF "sin"
  tan _ = IF "tan"
  cos _ = IF "cos"
  asin _ = IF "asin"
  atan _ = IF "atan"
  acos _ = IF "acos"
  sinh _ = IF "sinh"
  tanh _ = IF "tanh"
  cosh _ = IF "cosh"
  asinh _ = IF "asinh"
  atanh _ = IF "atanh"
  acosh _ = IF "acosh"

instance Real IntFloat where
  toRational _ = toRational 42

instance Enum IntFloat where
  succ _ = IF "succ"
  pred _ = IF "pred"
  toEnum _ = IF "toEnum"
  fromEnum _ = 42
  enumFrom _ = [IF "enumFrom"]
  enumFromThen _ _ = [IF "enumFromThen"]
  enumFromTo _ _ = [IF "enumFromTo"]
  enumFromThenTo _ _ _ = [IF "enumFromThenTo"]

instance Fractional IntFloat where
  (/) _ _ = IF "/"
  recip _ = IF "recip"
  fromRational _ = IF "fromRational"