1

Some years ago, I came across with haskell.org and played a little bit with its REPL. One of the expressions that I tried was just a sequence of numbers, separated by spaces, like 1 2 3 and I was surprised, since it didn't generate an error, but was evaluated and returned with some of that Haskell type descriptions, that, by the way, for a C and JAVA programmer seemed very intriguing. It may be just a nuance, but the result really left me curious, since an isolated set of parameters seemed to be a value by itself.

There were an example of an expression to try: foldr (:) [] [1, 2, 3]. I tried just (:) instead, and it still was a valid expression and returned some kind of structure.

Nowadays, the same site's REPL doesn't recognize these two expressions. And I also downloaded and installed Haskell Platform from here and it went to that GHCi REPL does not recognize it either. I tried this:

:set -XFlexibleContexts

and

:set -XAllowAmbiguousTypes

as it was suggested by REPL messages, but it didn't work.

I know this may be not an useful behaviour, but I still would like to reproduce it.

Thank you in advance.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Codefield
  • 13
  • 4
  • 1
    You sure you’re not just misremembering? – Ry- Feb 06 '18 at 01:54
  • Yes, I am sure, there was no commas, as in a vector. It really got me curious about it. – Codefield Feb 06 '18 at 01:58
  • `(:)` is a valid value, but `1 2 3` working is very surprising (because there is no default function instance of `Num`). – Ry- Feb 06 '18 at 01:59
  • Yes, but even that isn't working on GHCi. The result is this: – Codefield Feb 06 '18 at 02:06
  • `:1:1: error: * No instance for (Show (a0 -> [a0] -> [a0])) arising from a use of ``print' (maybe you haven't applied a function to enough arguments?) * In a stmt of an interactive GHCi command: print it` – Codefield Feb 06 '18 at 02:06
  • Ah, maybe the haskell.org REPL at the time showed type information if it couldn’t get an actual value. Is `:t 1 2 3` what you saw? `1 2 3 :: (Num (t1 -> t2 -> t3), Num t2, Num t1) => t3` – Ry- Feb 06 '18 at 02:16
  • I don't belive I saw `:t` at any moment, but `1 2 3 :: (Num (t1 -> t2 -> t3), Num t2, Num t1) => t3` looks really familiar. – Codefield Feb 06 '18 at 02:19
  • (I mean that that’s the output of `:t 1 2 3` on GHCi, not that `:t` would have appeared on haskell.org.) – Ry- Feb 06 '18 at 02:20
  • I added `:t` to `1 2 3` right now on CHCi and it worked. I wasn't aware about `:t`, I'm really new to Haskell. – Codefield Feb 06 '18 at 02:25
  • So i just need to add it, that's it? What exactly does `:t` mean? "type of"? – Codefield Feb 06 '18 at 02:26
  • Yep. Means exactly that in GHCi. Short for `:type`. – Ry- Feb 06 '18 at 02:30
  • Ok, thanks for that! But am I right when I say that `1 2 3` is of type "list of parameters"? Why to some things I need to add `:t`, even for `(:)`, that is a valid value? – Codefield Feb 06 '18 at 02:37
  • Now that I noticed it, GHCi doesn't show type information by default, while haskel.org REPL was showing it for every entry, at that time. – Codefield Feb 06 '18 at 02:54
  • @Codefield `1 2 3` is not a list of parameters. It is the application of the function `1` to two parameters `2` and `3`. In other languages you may write `1(2)(3)` or `1(2,3)`. The fact that the type error is more wonky than you might expect is related to how numeric literals work in Haskell, and this is what any good answer to this question will address (and I'm not). You need to add `:t` to some things because some things cannot be printed out (because they are not `Show`able), esp. functions, so you can use `:t` to print out their types and not their values. – HTNW Feb 06 '18 at 03:04
  • @HTNW Thank you for clarifying response. I was also trying Clojure REPL at that time and I think it 'helped' me to misunderstand the Haskell semantics. Clojure evaluates functions and operators as some kind of objects and since haskell.org REPL was 'adding' an implicit `:t` to every query, it was showing a similar behaviour of Clojure REPL: returning a number when entering number, a vector in case of a vector and even doing the same with functions. But also showing type, which made sense, since Haskell is stronly typed. – Codefield Feb 10 '18 at 01:22
  • @HTNW So when I typed `1 2 3` in and it returned the same thing, it looked like a value, since Clojure would acuse an error if trying to use `1` as a function. – Codefield Feb 10 '18 at 01:24
  • @HTNW I don't know if the 'concept' of an isolated list of arguments could be useful in any language, if existed, but now I know there's no such a thing in Haskell. Maybe it's a silly thing, but I thought it was awesome at that time, but I'm still excited about learning an almost purely functional language such Haskell and all the math that comes with it. – Codefield Feb 10 '18 at 01:29
  • @Codefield If you go ham with the language extensions, you can write `data HList (xs :: [*]) where { HNil :: HList '[]; HCons :: x -> HList xs -> HList (x:xs) }`, which is a list of things of different types. You can then have `class AppHList f a r | f a -> r where { appHList :: f -> HList a -> r }; instance AppHList x '[] x where { appHList = const }; instance (f ~ (a -> b), a ~ x, AppHList b xs r) => AppHList f (x : xs) r where { appHList f (HCons a xs) = appHList (f a) xs }`. This lets you pass an `HList` to a function: `args = HCons 5 $ HCons 4 HNil; one = appHList (-) args` – HTNW Feb 10 '18 at 02:40

1 Answers1

8

I suspect that you remember one of these two outcomes:

> :t 1 2 3
1 2 3 :: (Num (t1 -> t -> t2), Num t, Num t1) => t2
> 1 2 3

<interactive>:7:1: error:
    • No instance for (Num (t1 -> t0 -> a0)) arising from a use of ‘it’
        (maybe you haven't applied a function to enough arguments?)
    • In a stmt of an interactive GHCi command: print it

The former indicates that Haskell has attempted to interpret the literal 1 as a function that can be applied to the literals 2 and 3; the Num (t1 -> t -> t2) constraint says that 1 can be interpreted as a two-argument function, while the Num t and Num t1 constraints indicate that 2 and 3 can be interpreted as suitable arguments to this function.

The latter indicates the same thing, but goes on to say that furthermore it couldn't find such an instance that tells how to interpret a number as a function.

You can add one if you like; the least you'll need is something like this:

instance Num b => Num (a -> b) where fromInteger = pure . fromInteger

(This leaves the other Num methods undefined, meaning they will throw an error if you try to use them at function type.)

Following that declaration, the above two queries have slightly different results:

> :t 1 2 3
1 2 3 :: Num t => t
> 1 2 3
1

The former indicates that since there is now an in-scope method of interpreting number literals as functions, we can collapse all the previous constraints quite a bit. The latter produces a number "by accident" as it were: the monomorphism restriction defaults the polymorphic type Num t => t to Integer and prints the result.

The story with (:) is similar, I expect: probably you remember asking a :t query, which works just fine:

> :t (:)
(:) :: a -> [a] -> [a]

There's no standard way built-in of printing functions, though, so trying to "run" (:) on its own produces an error:

> (:)

<interactive>:14:1: error:
    • No instance for (Show (a0 -> [a0] -> [a0]))
        arising from a use of ‘print’
        (maybe you haven't applied a function to enough arguments?)
    • In a stmt of an interactive GHCi command: print it

This error says basically what I did: by default there's no way of showing functions. As before, you can add one if you like; a popular but lossy one is this:

> :m + Text.Show.Functions
> (:)
<function>

Slightly less lossy would be to do this instead (not in addition!):

> :m + Data.Typeable
> instance (Typeable a, Typeable b) => Show (a -> b) where show = show . typeOf

...but you can't apply it to (:) directly, as it works only with monomorphic functions.

> (:) 3
[Integer] -> [Integer]

Of course, the best is to use a lossless show implementation, but it is even more restrictive on which functions it can be used with: only monomorphic functions (as before) with finite domain (new restriction).

> :m + Data.Universe.Instances.Reverse
> enumFrom
[((),[()])]
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • First of all, thank you for such a detailed answer. I see you spend some time to make it complete and explain every topic. But I hope I don't disappoint you saying that I don't want to make Haskell evaluate `1 2 3` or `(:)` 'overwriting' its usual behaviour, to not lose generality. I know that it seems that the title asks for that, but the thing is that I misunderstood the Haskell semantics and I wanted a behaviour that simply doesn't exist. Since haskell.org REPL was sort of appending `:t` to every query implicitly, `1 2 3` seamed to me as a special value, but I know what was happening. – Codefield Feb 10 '18 at 01:46
  • I still learned a lot with your answer and still can make use `:t` to simulate that old behaviour from haskell.org REPL. Off course, maybe there's a way of making every query a `:t` query, but off course it doesn't look necessary, since just write it is easy enough (and not writing, to 'disable' that behaviour). Thanks again. – Codefield Feb 10 '18 at 01:51