Search code examples
haskelltypesread-eval-print-loopexpression-evaluation

How to make "1 2 3" a valid expression in a Haskell REPL environment?


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.


Solution

  • 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
    [((),[()])]