[m-users.] Printing any value with io.print

Volker Wysk post at volker-wysk.de
Tue Mar 17 04:08:07 AEDT 2020


Am Montag, den 16.03.2020, 06:03 +1100 schrieb Zoltan Somogyi:
> 
> On Sun, 15 Mar 2020 18:46:31 +0100, Volker Wysk <post at volker-wysk.de>
> wrote:
> > The io.print predicates are able to print values of any type. I'm
> > curious how this is possible. In Haskell, the types must be in the
> > "Show" type class and you define a member function, which handles
> > values of the type.
> 
> Mercury treats every type as Haskell would treat a type that had
> "deriving (Eq,  Ord, Show, Read)" after its definition. In other
> words, in Mercury,
> the unification, comparison, print and read operations are defined
> for values
> of all types. (There is a minor niggle in that you cannot unify or
> compare
> higher order values, so the compiler-defined unify and compare
> operations
> on higher order values throw exceptions, and reading and writing them
> don't make much sense either.)

I was confused, because the print predicate's type doesn't depend on
any type classes, like it would in Haskell. It are "implicit type
classes", like you explained. And this doesn't work _completely_,
because of that minor niggle...


> > How does io.print know how to handle values of ANY
> > type...?
> 
> When your code has a call to io.print, and passes e.g X to be
> printed,
> the compiler adds an implicit argument to the call that tells it the
> type of X.
> But io.print is not special; for every predicate or function that has
> a type
> variable in its signature, the compiler passes along an extra
> argument
> that gives the identity of the concrete type that is bound to the
> type variable
> on this call. We call this compiler-generated argument a "type_info",
> since it
> gives the callee information about a type. And if a predicate or
> function
> has N type variables in its signature, the compiler passes N extra
> type_info
> arguments, one for each type variable.

And this doesn't seem to be inefficient... I was surprised about how
fast Prolog and Mercury are. Haskell sure is slower. This might be
because of the String type and because of lazy evaluation...

> The type_info structures are small, just enough to describe the
> structure
> of the type in terms of type constructors. (For example, "map(string,
> list(int))"
> has four type constructors, map/2, string/0, list/1 and int/0.) For
> each
> type constructor, they contain a pointer to a larger data structure
> called
> a type_ctor_info that specifies whether the type constructor is a
> builtin
> and if so which builtin (e.g. string or int), and if not, what
> function symbols
> the user-defined type constructor defines, and how their arguments
> are
> represented. These data structures were designed specifically to
> provide
> the information that operations such as io.print need to work.
> 
> If you want to know more about this, we wrote this up in a paper
> a long time ago. The paper is available at
> 
> http://mercurylang.org/documentation/papers.html#rtti_ppdp
> 
> The details have changed a bit since then, but the principles remain
> the same.
> 
> > I need to write an algebraic data type, as a term, to a string,
> > just
> > like io.print does, when it writes it to an output stream. How
> > should
> > this be done?
> 
> I am not sure what this is asking, but if it is asking "how can I
> print
> arbitrary values so the output goes not to an output stream, but to
> a string", then ...
> 
> > I couldn't find a string_output_stream feature...
> 
> ... you should use stream.m, which specifies an interface for things
> that can be written to as a stream, and string.builder, which
> specifies
> how you can write to a string.builder as a stream. Note: writing
> to a string as a stream as a bad idea, because the constant appending
> to the end of a string yields O(N^2) complexity. With string.builder,
> you build up a list of string fragments, which you can convert into
> a string when you are done appending. This is O(N).
> 
> > Is the
> > "format anything" capability of io.print available outside of
> > io.print?
> 
> As the above shows, the answer is "yes". And the raw operations
> on which io.print is built is available in the deconstruct module
> of the Mercury standard library.

So, when I wanted to write something like one of the print predicates,
I would use the deconstruct module and get access to the type_info
structures...

I'm not sure what's better - the Mercury or the Haskell way. The latter
appears to be cleaner to me...

Bye,
Volker




More information about the users mailing list