pre-srfi / static-scheme

11 stars 0 forks source link

ML-style modules vs Haskell-style type classes: Existential types needed? #25

Open mnieper opened 3 years ago

mnieper commented 3 years ago

John suggests using (multi-parameter) type classes for Steme. I am wondering whether we need more of Haskell's advanced features to emulate ML-style modules, in particular, whether we also have to incorporate existential types.

lassik commented 3 years ago

NB: Here's a work-in-progress language with effects and first-class modules: https://broom.readthedocs.io/en/latest/introduction.html

mnieper commented 3 years ago

This language does not seem to have complete HM type inference but the reasons why they left it out may not be related to modules and effects.

What do you call a first class module? In that language as in SML, a module seems to be a citizen of the same status as a macro transformer in Scheme, is it right?

This actually makes me wonder how much of the ML module system we already have through Scheme macros or how much could actually already be expressed using macros.

lassik commented 3 years ago

What do you call a first class module?

I didn't get the chance to read in detail yet, but Broom has implemented modules via records, which is not the case in classic ML. The manual says "Modules are blocks that produce records of their bindings instead of the value of the last expression". There's something Python/JS/Smalltalk-like about that.

In that language as in SML, a module seems to be a citizen of the same status as a macro transformer in Scheme, is it right?

As far as I can tell, that is a good analogy. However, can't Scheme macros (syntax-case or ?r-macro-transformer) evaluate arbitrary Scheme code at the call site? Defining a module in SML doesn't have side effects other than defining the module...

This actually makes me wonder how much of the ML module system we already have through Scheme macros or how much could actually already be expressed using macros.

ML modules are intertwined with the rest of the type system, so you'd have to pass type information across Scheme library boundaries. It's not clear whether Steme libraries themselves should support parameters like C++ templates, with a parametric import.

mnieper commented 3 years ago

In that language as in SML, a module seems to be a citizen of the same status as a macro transformer in Scheme, is it right?

As far as I can tell, that is a good analogy. However, can't Scheme macros (syntax-case or ?r-macro-transformer) evaluate arbitrary Scheme code at the call site? Defining a module in SML doesn't have side effects other than defining the module...

A (procedural) macro can only run arbitrary code at expand-time (compilation time). And SML has to run code to instantiate the module at compile-time as well, of course. Procedural Scheme macros just give us the freedom to run more code than just what's already in the compiler. (John's prime example is always a Fortran embedded in Scheme. :smiley:)

This actually makes me wonder how much of the ML module system we already have through Scheme macros or how much could actually already be expressed using macros.

ML modules are intertwined with the rest of the type system, so you'd have to pass type information across Scheme library boundaries. It's not clear whether Steme libraries themselves should support parameters like C++ templates, with a parametric import.

Don't let us mix modules with libraries as they are defined by Scheme. A module can be a smaller entity and can actually be a thing exported by libraries. (The modules of Chez and Unsyntax are actually such (expand-time) entities that can be bound to identifiers and im- and exported. I have no experience with SML modules so I am not sure whether we can already do with these Scheme modules that we can do with SML modules (modulo static typing, of course).)

lassik commented 3 years ago

It may be that Scheme/Steme interop places some constraints on whether we can merge modules and libraries or not. Maybe there would be difficulties on the Scheme side if we do merge them.

mnieper commented 3 years ago

The following is, as I think, a good object of study to measure whether modules, type classes, or whatever other fancy gadget are actually expressive enough for those cases they intend to cover:

Problem

Write a function power that takes a dimension, a square matrix of that dimension over a generic ring, and an exponent and that returns the power of the matrix to the given exponent.

The point of this problem is, of course, how to cope with the ring structure that is in the background. Isn't it the idea of typeclasses that the ring doesn't have to be passed as an extra argument? Is it the same with modules? But what about the case that the exponent is zero so that we expect the identity matrix as a result, which has no relation to the entries of the original matrix...

mnieper commented 3 years ago

It may be that Scheme/Steme interop places some constraints on whether we can merge modules and libraries or not. Maybe there would be difficulties on the Scheme side if we do merge them.

At least ML modules and Scheme libraries seem to serve different purposes. So let us not conflate the two notions, at least not in this thread. This questions in this thread make perfect sense even when the whole source code just resides in one file.

lassik commented 3 years ago

ML structures (which are concrete modules) can be "opened" to import all their bindings into the current environment, which is similar to a Scheme import. Here's a good intro on SML modules which also goes into typeclass-like stuff: https://www.cs.cmu.edu/~rwh/introsml/modules/sigstruct.htm

mnieper commented 3 years ago

ML structures (which are concrete modules) can be "opened" to import all their bindings into the current environment, which is similar to a Scheme import. Here's a good intro on SML modules which also goes into typeclass-like stuff: https://www.cs.cmu.edu/~rwh/introsml/modules/sigstruct.htm

Unless extended by a particular implementation, a Scheme library cannot be imported locally. However, macros can create local bindings, which is one reason why I mentioned them in the context of modules.