gluon-lang / gluon

A static, type inferred and embeddable language written in Rust.
https://gluon-lang.org
MIT License
3.21k stars 145 forks source link

std function to print un-Showable types like the REPL does #564

Open lilyball opened 6 years ago

lilyball commented 6 years ago

The REPL seems to be able to print everything, but looking through the standard library documentation, I don't see how to print things that aren't Show. It would be nice if there was some way to convert arbitrary values into String in the same way the REPL does.

For example, I'm going through the book right now and I ran into the following example:

// type <identifier> <identifier>* = <type> in <expression>
type MyOption a = | None | Some a
let divide x y : Int -> Int -> MyOption Int =
    if (x / y) * y == x then
        Some (x / y)
    else
        None
in divide 10 4

This example can't be pasted into the REPL, so I put it in a file, and I can "run" that file, but there's no output. I wanted to tweak it to show me the results, but I don't see anything I can put on that last line to give me the expected output None.

Marwes commented 6 years ago

It isn't possible to print the value exactly how the repl does it since a function as described wouldn't have any type information available. Because of that the displayed value won't show Some None etc, it will only display the tag of the variant 0, 1, 2 .... (The repl does know about the type and can therefore do a pretty good job).

Basically we just add another function like the trace function in std.debug https://github.com/gluon-lang/gluon/blob/master/vm/src/debug.rs which uses format! instead.

lilyball commented 6 years ago

Could we do something funky like have a special implicit type like

#[implicit]
type DebugShow a = { debugShow : a -> String }

and then have the compiler synthesize an implementation of this for every type that you try to call it with?

Or alternatively, and I have no idea how Gluon macros actually work, but maybe have a compiler-provided macro dump! that synthesizes the output right there using the type information available to the compiler.

Marwes commented 6 years ago

The issue is this

let my_polymorphic_function x : a -> () =
    debug_show x // Since types are erased we have no idea what the type of the value is here
    ()

my_polymorphic_function "abc"
my_polymorphic_function 1
my_polymorphic_function { x = 1, y = 2 }

and then have the compiler synthesize an implementation of this for every type that you try to call it with?

This would be possible if functions were monomorphized but currently generics are just type erased (so there is only one instance of each function which can accept any type). While monomorphization works well for Rust, gluon's support for higher ranked types would actually make it impossible to monomorphize functions in general so it wouldn't fix this problem either (at least not completely).

With https://github.com/gluon-lang/gluon/pull/553 it will be possible to do #[derive(Show)] which should improve this a bit as well.