arturo-lang / arturo

Simple, expressive & portable programming language for efficient scripting
http://arturo-lang.io
MIT License
673 stars 29 forks source link

Introduce new `:module` type #1616

Closed drkameleon closed 2 months ago

drkameleon commented 2 months ago

Description

This PR's goal is to experiment a bit with an idea of modules (= scope-aware singletons, kind-of, that expose to the public scope only selected methods as functions).

A similar pure-Arturo implementation would be this:

module: function [content].inline[
    public: function [arguments, body][
        meth: method arguments body
        __\exports: __\exports ++ @[@[var'meth, arguments, body]]
        return var'meth
    ]

    __: #[]
    __\id: unique.id "module_"
    __\type: to :type __\id
    __\exports: []

    __\getExportable: function [m][
        __\i: 0
        while [__\i < size __\exports][
            if __\exports\[__\i]\0 = var'm ->
                return @[__\exports\[__\i]\1 __\exports\[__\i]\2]
            __\i: __\i + 1
        ]
        return null
    ]!

    define __\type content

    let ~"__|__\id|" to __\type []

    loop methods var ~"__|__\id|" 'meth [
        __\mm: get var ~"__|__\id|" meth
        __\got: __\getExportable var'__\mm
        unless null? __\got [
            let meth function __\got\0 (@[to :label "this"]) ++ (@[to :word ~{__|__\id|}]) ++ [do] ++ @[__\got\1]
        ]
    ]
]

module [
    privateMethod: method [x][
        print ["This is a private method, with:" x]
    ]

    publicMethod: public [][
        print "Running a public method"
        this\privateMethod "hello"
        print "And back in public space!"
    ]
]!

publicMethod

Now, the PR here will attempt to take this a step further:

1) Make it so that the whole thing is written in Nim (for efficiency & speed) 2) Experiment a bit with the idea of distinct value type (Module) that can be e.g. exported and will give us even more flexibility.

ℹ️ Final implementation

Let's imagine we create this package/file (let's call it importedpkg.art):

export module [
    privateMethod: method [x][
        print ["This is a private method, with:" x]
    ]

    publicMethod: method.public [z][
        print "Running a public method"
        this\privateMethod "hello"
        print "And back in public space!"
    ]
]

Then this, would be how we'd import it from another file -- supposing the 2 files reside in the same folder:

import {importedpkg}!

publicMethod 10

privateMethod ; this will throw an error!

So, as you can see publicMethod is available in our current/global scope, while privateMethod is not 😉

Type of change

drkameleon commented 2 months ago

Ready to merge! 🚀