Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Haskell like Prelude modules in a module in raku

Tags:

haskell

raku

I'm writing a drawing package with some parts, and I have operators and data types scattered througout. However I don't want the users to add the corresponding modules every time, since it would be quite messy, for instance I'd have a Point class, a Monoid role and a Style class in different paths like this

unit module Package::Data::Monoid;
# $?FILE = lib/Package/Data/Monoid.pm6

role Monoid {...}
unit module Package::Data::Point;
# $?FILE = lib/Package/Data/Point.pm6

class Point {...}
unit module Package::Data::Style;
# $?FILE = lib/Package/Data/Style.pm6

class Style {...}

I would like to have a haskell like prelude in lib/Package/Prelude.pm6 with the effect that I can write such scripts

use Package::Prelude;

# I can use Point right away, Style etc...

instead of doing

use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;

# I can too use point right away, but for users not knowing the
# inner workings it's too overwhelming

I've tried many things:

  • This version doesn't give me the right effect, I have to type the whole path to point, i.e., Package::Data::Point...
unit module Package::Prelude;
# $?FILE = lib/Package/Prelude.pm6
use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;
  • This version gives me the Point right away, but I get problems with the operators and so on, also I would just like to add automatically everything from the exported routines in the mentioned example packages.
# $?FILE = lib/Package/Prelude.pm6
use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;

sub EXPORT {
  hash <Point> => Point
     , <Style> => Style
     , <mappend> => &mappend
     ...
}

Do you people know a better and quick way of getting such a prelude-like file?

like image 225
margolari Avatar asked Apr 12 '20 23:04

margolari


People also ask

What is a prelude in Haskell?

Prelude is a module that contains a small set of standard definitions and is included automatically into all Haskell modules. The documentation of prelude from GHC can be found here . If you wish to program without a prelude or to use a custom version of it you can suppress its automatic inclusion in several ways.

What is a module in Haskell?

If you have worked on Java, then you would know how all the classes are bound into a folder called package. Similarly, Haskell can be considered as a collection of modules. Haskell is a functional language and everything is denoted as an expression, hence a Module can be called as a collection of similar or related types of functions.

What is a raku module?

In Raku module can also refer to a type of package declared with the module keyword (see Module Packages and the examples below) but here we mostly mean "module" as a set of source files in a namespace. zef is the application used for installing modules in Raku.

What does depends mean in raku?

It is used to describe the module in the Raku ecosystem. The depends, build-depends, and test-depends sections include different modules that are used in those phases of the of installation. All are optional, but they must obviously contain the modules that are going to be needed in those particular phases.


Video Answer


1 Answers

Using EXPORT is in the right direction. The key things to know are:

  • Imports are lexical
  • We can use introspection to obtain and access the symbols in the current lexical scope

So the recipe is:

  • use all the modules inside of EXPORT
  • Then extract all the imported symbols and return them as the result from EXPORT

As an example, I create a module Foo::Point, including an operator and a class:

unit module Foo::Point;

class Point is export {
    has ($.x, $.y);
}

multi infix:<+>(Point $a, Point $b) is export {
    Point.new(x => $a.x + $b.x, y => $a.y + $b.y)
}

And, just to demonstrate it can work with multiple modules, also a Foo::Monad:

unit module Foo::Monad;

class Monad is export {
    method explain() { say "Just think of a burrito..." }
}

The goal is to make this work:

use Foo::Prelude;
say Point.new(x => 2, y => 4) + Point.new(x => 3, y => 5);
Monad.explain;

Which can be achieved by writing a Foo::Prelude that contains:

sub EXPORT() {
    {
        use Foo::Point;
        use Foo::Monad;
        return ::.pairs.grep(*.key ne '$_').Map;
    }
}

There's a few oddities in here to explain:

  1. A sub has implicit declarations of $_, $/, and $!. Exporting these would result in a compile-time symbol clash error when the module is use'd. A block only has an implicit $_. Thus we make our life easier with a nested bare block.
  2. The grep is to make sure we don't export our implicitly declared $_ symbol (thanks to the nested block, it's the only one we have to care about).
  3. :: is a way to reference the current scope (etymology: :: is the package separator). ::.pairs thus obtains Pair objects for each symbol in the current scope.

There's a speculated re-export mechanism that may appear in a future Raku language release that would eliminate the need for this bit of boilerplate.

like image 68
Jonathan Worthington Avatar answered Oct 21 '22 17:10

Jonathan Worthington