Nemerle, anyone?



  • So, blundering wildly around the world of Mono, I came across this thing called Nemerle.

    module Program
    {
      class Initializer
      {
        public Name : string { get; set; }
        public event HelloHandler : Action[string];
        
        public SayHello() : void 
        {
          HelloHandler($"Hello, $Name!");
        }
        
        public static Test() : void
        {
          def hellower = Initializer() <- 
            {
              Name = "David Hilbert";
              HelloHandler += Console.WriteLine;
            };
     
          hellower.SayHello();
        }
      }
     
      Main() : void
      {
        Initializer.Test();
      }
    }
    

    It seems to be some kind of terse version of C# with type inference (and some other features). It even has macros. From my five minute observation, it looks a bit functional-y in parts, maybe even a bit like Rust (although obviously not the borrow checker you pedantic dickweeds).

    def factorial(n, acc)
    {
      | (0, _)
      | (1, _) => acc
      | _      => factorial(n - 1, n * acc)
    }
    def partedFactorial = factorial(_, 1);
    Console.WriteLine(partedFactorial(5));
    

    Seems reasonable to me, wondering if anyone has any thoughts, opinions, experience with it? Is it sufficiently better than C# to make a compelling alternative? Or is it just the programming equivalent of an unshaven neck?



  • I was wondering WTF is going on as I started reading code... Am I that rusty? Apparently not.

    Whatever this language is, it's not worth the effort IMO. You'll make non-portable code that will be tied to an open source project with a cloudy future ahead of it. Microsoft has so far been a pretty good steward of C#. Their upcoming compiler and language updates are exciting enough. You don't need this shit, is my bottom like, I guess.



  • The fact that the wiki is broken on their main site is a bit of a red flag, I'll admit...


  • I survived the hour long Uno hand

    @tar said:

    public event HelloHandler : Action[string];

    So we're constructing... looks like... an event handler, by the naming scheme... which consists of an action and takes a string. Okay, fair enough...

    @tar said:

    HelloHandler($"Hello, $Name!");

    So when you call the SayHello method, it invokes the event handler. So there's no actual event being thrown? Okay...then.....

    Also where does $Name come from? Magic?

    @tar said:

    Name = "David Hilbert";

    What seriously.

    @tar said:

    HelloHandler += Console.WriteLine;

    Wait what. Why? Why not... what?What?

    So the key piece of code to this example, the HelloHandler event handler, is defined in bits and drabs all over the fucking place instead of being someplace easy to find?

    Why do you want this?


  • FoxDev

    @Yamikuronue said:

    So when you call the SayHello method, it invokes the event handler. So there's no actual event being thrown?

    The whole event syntax is just sugar for registering callbacks ;)


  • I survived the hour long Uno hand

    Why?


  • FoxDev

    You'll have to ask the language designers I'm afraid ;)


  • I survived the hour long Uno hand

    I mean, why the fuck do you WANT this? That's legit the only question I've got for this thread.

    I mean, I can write javascript like

    function onClick = {
        alert("Clicked!");
    }
    
    $(".btn").click(onClick);
    
    $( document ).ready(function() {
        while(true)  onClick();
    });
    

    But why the fuck would you want that?


  • ♿ (Parody)

    Eh...looking at their page, it looks like a contrived and silly example. It's supposed to be showing how you'd to OOP in their multi-paradigm language. And they hide the constructor, I'm guessing, to show off as many features as possible in a short piece of code.


  • I survived the hour long Uno hand

    Yeah but when I want people to adopt something, I tend to show examples of why you WOULD want to use this.... you know?

    Like, on jQuery's home page:

    var hiddenBox = $( "#banner-message" );
    $( "#button-container button" ).on( "click", function( event ) {
      hiddenBox.show();
    });
    

    That shows something I can't do concisely in vanilla JS, demonstrating the simpler syntax that makes jQuery ubiquitous. What's the killer feature demonstrated in that code? Shitty syntax and confusing examples?


  • FoxDev

    As @boomzilla says, their example is highly contrived. However, syntax like C#'s event comes in handy for all sorts of GUI-related stuff, and more besides.

    Example: A Discourse bot.
    Much like @sockbot, a Discourse bot written in C# will have modules that need to be able to respond to messages and notifications. Now, this could be done by passing callbacks around and all that malarkey. Or, the core of the bot could expose this (partial) interface (assuming these classes are all defined)

    public interface IDiscourse
    {
        public event Action<Topic, Post> OnMessage;
    }
    

    And then each module just does

    iDiscourse.OnMessage += HandleMessage;
    

    And the compiler does all the heavy lifting for you ;)



  • Yes mistress Cyber-harinezumi; @accalia's queen, I shall appear as summoned.


  • FoxDev

    That bot is scarily good at getting that right…


  • I survived the hour long Uno hand

    I see. So despite naming it "handler", the event object more or less corresponds to an actual event. An event called helloHandler. += here is overloaded to mean "attach a listener to".

    Everything old is new again. Bah humbug.


  • FoxDev

    @RaceProUK said:

    Now, this could be done by passing callbacks around and all that malarkey.

    well there's other solutions...

    • You could grovel the modules by reflection and if they define an OnMessage method register it for them
    • You could have the modules call a method on the controller as part of their initialization that registers their callbacks
    • you could hire a tiny imp to move bits for you.
    • or you could do something sane and use the CLI event system the way it was intended to be used.

  • I survived the hour long Uno hand

    eventProducer.addEventListener(this)
    

  • FoxDev

    for the record, the first bullet point is what @sockbot actually does. of course JS is practically made for reflection.

    i realize there were other, possibly better methods. i decided to ignore them. :-P



  • Yes master Sultanatrix of Swypos; @RaceProUK's queen, I shall appear as summoned.


  • ♿ (Parody)

    @accalia said:

    or you could do something sane and use the C# event system the way it was intended to be used.

    NB: This isn't C#. It's its own language targeting the CLI, though I guess it has a lot of similarity, as they say, "One may start using it as an advanced C# and then, as learning goes on, employ a range of cool features enabling metaprogramming and functional programming."


  • FoxDev

    i sit corrected.

    edited post for correctness (i hope)


  • FoxDev

    @accalia said:

    for the record, the first bullet point is what @sockbot actually does. of course JS is practically made for reflection.

    Plus I don't recall JS having interfaces as a language feature; its object system is prototype-based rather than class/interface based.
    @Yamikuronue said:
    I see. So despite naming it "handler", the event object more or less corresponds to an actual event. An event called helloHandler. += here is overloaded to mean "attach a listener to".

    Handler is nothing more than a naming convention, one that I think originated in VB; it's quite a common convention in MS technologies.
    Also, technically, there's no 'event object'; it's actually a special form of property.
    @Yamikuronue said:
    ```
    eventProducer.addEventListener(this)

    [s]Not sure how typesafe that is; the CLI system is very rigorous in that respect.[/s]
    That requires a complete class definition that implements the interface in question; the CLI system only requires a single method.
    
    <!--​ Emoji'd by MobileEmoji 0.2.0-->

  • 🚽 Regular

    @Yamikuronue said:

    += here is overloaded to mean "attach a listener to".
    You can blame C# for this one, not Nemerle.



  • Wikipedia has some maybe-less-contrived source code examples. I think the metaprogramming/macro system is probably the 'killer feature' compared to vaniila C#.



  • @RaceProUK said:

    The whole event syntax is just sugar for registering callbacks

    But C# heavily supports and encourages use of real, actual events, so that's about the most confusing naming possible.


  • FoxDev

    The way I see it, events is just fancypants talk for callbacks; at the nuts-n-bolts level, they work essentially the same.



  • @RaceProUK said:

    The way I see it, events is just fancypants talk for callbacks; at the nuts-n-bolts level, they work essentially the same.

    Events are broadcast to all listeners, callbacks are one-at-a-time. I think they're different enough to need different keywords/concepts... JavaScript, for example, frequently mixes both.


  • FoxDev

    True; I guess the distinction is really single-callback (callback) or multi-callback (events).


  • FoxDev

    while i'm not disagreeing with the distinction in general there is also the concept of "Call this sequence of handlers in order until you reach the end of the list or one of the handlers tells you that they handled it"

    i see that concept also called events (or more commonly event propagation)

    that's sufficiently to an essentially asynchronous broadcast to everyone who is interested that i really think it deserves special mention (or maybe it's own (better) term)


  • Java Dev

    Definitely a distinction there, as:

    1. The order of the handlers matters
    2. The handlers cannot be called concurrently, EG in a multithreaded environment.


  • @tar said:

    $"Hello, $Name!"

    Oh fuck, they took language design classes from QBasic. RUN!

    @Yamikuronue said:

    Everything old is new again.

    Not gonna pull out the calendar on you, but that syntax has been there in C# for, dunno, ten years? 15?

    And now it makes me think whether VB6 and lower had it too, but I don't remember.

    @Yamikuronue said:

    eventProducer.addEventListener(this)

    Except you don't have the stupid Java "listener objects". You attach methods.


  • FoxDev

    @Maciejasjmj said:

    And now it makes me think whether VB6 and lower had it too, but I don't remember.

    I know VB6 had something like it; it's where I first met the event-driven model



  • This seems like fun, though, eh?

    macro ReverseFor (i, begin, body)
    syntax ("ford", "(", i, ";", begin, ")", body)
    {
      <[ for ($i = $begin; $i >= 0; $i--) $body ]>
    }
    
    //...
    
    ford (i ; n) print (i);
    
    

  • BINNED

    :facepalm:



  • @Maciejasjmj said:

    Oh fuck, they took language design classes from QBasic. RUN!

    Not sure why $-interpolation is any worse here than it would be in, say, Perl or PowerShell...



  • "Power! UNLIMITED POWER !!
    MuaahuaHUAHUAHAHAHA!!!!1"
    *lightning*
    "THEY SAID I WAS MAAAD!?!$"
    *thunder*


  • ♿ (Parody)

    @tar said:

    Not sure why $-interpolation is any worse here than it would be in, say, Perl or PowerShell...

    Yeah, I saw that and thought, "perl."


  • Discourse touched me in a no-no place

    @boomzilla said:

    Yeah, I saw that and thought, "perl."

    That's what we need! A C# like language with perl features!



  • @CHUDbert said:

    A C# like language with perl features!

    PowerShell? It's got access to the whole .NET API...



  • So, against everyone's better judgement, I decided to download this and have a play with it anyway, in the interests of :wtf:.

    using Nemerle.IO;
    using System.Threading.Tasks;
    
    
    printf("Hello World\n");
    
    def proc = () => {
        printf("Starting task!\n");
        "done!";
    };
    
    def task = Task.Factory.StartNew(proc);
    
    while(!task.IsCompleted) {
    //    printf(".");
    };
    
    printf("task.Result: %s\n", task.Result);
    
    1. It ships as a load of managed dlls so you can just unzip the whole mess anywhere you have mono/.NET set up, and the compiler basically 'just works' ([mono] ncc).
    2. You have access to the whole of the .NET framework (e.g. I'm using System.Threading.Tasks for shits & giggles), which is handy—playing will this will improve my vanilla C#.
    3. Kinda looks like C# except where it doesn't. The single biggest difference is def variable: type rather than type variable, to specify types. Also, there's no return keyword, which is less of a :wtf: that I thought it'd be. Has ML-style pattern matching, which I haven't played with much either.
    4. You don't need to have a public static Main() unless you want to. I didn't bother for this example.
    5. Type inference is really nice though, the compiler can figure out what you meant 90% of the time. Also Nemerle.IO.printf() is typesafe and will complain if you try to print a string with %d.
    6. The macros are just crazy awesome, you can wholesale invent new language syntax, keywords and operators if that's your bag. Haven't really done much other than play with them yet though.
    7. Documentation is a bit rough, and Google can't offer much help if you get lost. (Yay!)


  • @tar said:

    You don't need to have a public static Main() unless you want to. I didn't bother for this example.

    What's the entry point if you don't?


  • Discourse touched me in a no-no place

    @blakeyrat said:

    What's the entry point if you don't?

    Looks like it assembles it out of all the non-declaratory parts of the program, so it pretends to be some sort of scripting language.



  • @dkf said:

    it pretends to be some sort of scripting language.

    This. Obvious next test is to try to link together two files with non-declaratory parts, see what happens...



  • Oh, so you can just have loose code outside of classes and such? Does it still have like an "entry point namespace" you have to use, or does the loose code go outside of namespaces, too?

    That's very non-C#-like.



  • @blakeyrat said:

    does the loose code go outside of namespaces, too?

    You can in fact just dump loose code just into the root namespace, directlly after your usings, if you like. I would guess that for a project of any size, you'd probably want a Main() somewhere for convention's sake. I'm going to do some further experimentation with this later...



  • Results of my test. Any code at the toplevel of a source file is wrapped in a Main() function. Trying to link two files with toplevel code causes an error.

    test2.n|3 col 1| 5:1: error: more than one entry point (Main function) found, e.g. types test1 and test2
    test2.n|3 col 1| 5:1: hint: you can use -main TypeName switch to disambiguate selection
    

    However, each of these autogenerated Main() functions is considered to be a member of a type whose classname is the same as the filename (e.g. test1.ntest1.Main(), test2.n
    test2.Main()), so you can add -main test1 and then the code compiles, and runs the toplevel code just from test1.n.

    Then you can add an explicit call to test2.Main(); to the end of test1.n, and get the toplevel code from both source files to run. I wouldn't be surprised if this was well into the realm of compiler-specific/undefined behaviour, and you wouldn't want to rely on it in production code, but it is sort of logical...



  • OK, well this is becoming something of a tradtition, then. If this code looks a bit familiar, that's because I originally wrote it in Rust...

    // Conway's game of life in Nemerle
    
    using printf = Nemerle.IO.printf;
    
    class Life {
    
        public static WIDTH = 16;
        public static HEIGHT = 10;
    
        variant Cell {
            | ALIVE
            | DEAD
    
            public update(count: int): Cell {
                match(this, count) {
                    | (DEAD, 3) => ALIVE()
                    | (ALIVE, 2) => ALIVE()
                    | (ALIVE, 3) => ALIVE()
                    | (_, _) => DEAD()
                }
            }
    
            public isAlive(): bool {
                match(this) {
                    | DEAD => false
                    | ALIVE => true
                }
            }
        }
    
        mutable grid: array[2, Cell];
    
        public this() {
            grid = array(HEIGHT, WIDTH);
            foreach(i in [0..WIDTH - 1]) {
                foreach(j in [0..HEIGHT - 1]) {
                    grid[j, i] = Cell.DEAD();
                }
            }
        }
    
        public print(): void {
            def hframe = string('-', WIDTH);
            printf("+%s+\n", hframe);
            foreach(j in [0..HEIGHT - 1]) {
                mutable line = "";
                foreach(i in [0..WIDTH - 1]) {
                    line += match(getCell(i, j)) {
                        | DEAD => "."
                        | ALIVE => "X"
                    };
                }
                printf("|%s|\n", line);
            }
            printf("+%s+\n", hframe);
        }
    
        public update(): void {
            def newGrid = array(HEIGHT, WIDTH);
            foreach(i in [0..WIDTH - 1]) {
                foreach(j in [0..HEIGHT - 1]) {
                    newGrid[j, i] = updateCell(i, j);
                }
            }
            grid = newGrid;
        }
    
        public run(): void {
    
            setCell(2, 2, Cell.ALIVE());
            setCell(3, 3, Cell.ALIVE());
            setCell(1, 4, Cell.ALIVE());
            setCell(2, 4, Cell.ALIVE());
            setCell(3, 4, Cell.ALIVE());
    
            setCell(9, 6, Cell.ALIVE());
            setCell(10, 6, Cell.ALIVE());
            setCell(11, 6, Cell.ALIVE());
            setCell(11, 7, Cell.ALIVE());
            setCell(10, 8, Cell.ALIVE());
    
            foreach(_ in [1 .. 10]) {
                print();
                printf("\n");
                update();
            }
    
        }
    
        private cellCoords(x: int, y: int): (int * int) {
            def xx = (unchecked (x + WIDTH) :> uint) % WIDTH;
            def yy = (unchecked (y + HEIGHT) :> uint) % HEIGHT;
            (xx :> int, yy :> int);
        }
    
        private getCell(x: int, y: int): Cell {
            def (xx, yy) = cellCoords(x, y);
            grid[yy, xx];
        }
    
        private setCell(x: int, y: int, c: Cell): void {
            def (xx, yy) = cellCoords(x, y);
            grid[yy, xx] = c;
        }
    
        private getCellCount(x: int, y: int): int {
            mutable result = 0;
            foreach((i, j) in neigbours(x, y)) {
                when(getCell(i, j).isAlive()) {
                    result++;
                }
            }
            result;
        }
    
        private updateCell(x: int, y: int): Cell {
            def c = getCell(x, y);
            def n = getCellCount(x, y);
            c.update(n);
        }
    
        private neigbours(x: int, y: int): array[(int * int)] {
            array[(x - 1, y - 1), (x, y - 1), (x + 1, y - 1),
                (x - 1, y), (x + 1, y),
                (x - 1, y + 1), (x, y + 1), (x + 1, y + 1)]
        }
    
    } // class Life
    
    def life = Life();
    life.run();
    
    

    Nemerle seems to like | blah => blah for the arms of its match statements where Rust is all about blah => blah, which I think I prefer, but it's not a deal breaker. Also a lot less fiddling with references and borrowing and object lifetimes in this iteration—maybe it's less efficient as a result though, who knows?



  • I'm actually quite liking the portability aspect of this mono/.NET stuff. It's an .exe and some .dlls—build it all on Linux, copy onto Windows and run it directly from there!

    (Hmm... I should try running Paint.NET on mono tomorrow...)


  • Fake News

    Nemerle's match syntax is the same as that of F#. I'm guessing it borrows other elements as well.



  • @lolwhat said:

    Nemerle's match syntax is the same as that of F#. I'm guessing it borrows other elements as well.

    For a language which is essentially C# on steroids, it makes sense for it to 'leverage' features of other .NET languages. They're claiming a Lisp/Scheme provenance for their macro system though...



  • Experimenting with YAML serialization using some random YamlDotNet library I found on github. (It seems to be the least broken/abandoned one for .NET out there currently...)

    using System.IO;
    
    using YamlDotNet.Core;
    using YamlDotNet.Core.Tokens;
    using YamlDotNet.Serialization;
    
    using print = Nemerle.IO.print;
    using printf = Nemerle.IO.printf;
    using Nemerle.Utility;
    
    [Record]
    public class Address {
        public this() {}
        [YamlAlias("street")]
        public Street: string { get; set }
        [YamlAlias("city")]
        public City: string { get; set }
        [YamlAlias("postcode")]
        public Postcode: string { get; set }
    };
    
    [Record]
    public class Test {
        public this() {}
        public name: string { get; set; }
        public age: int { get; set; }
        public address: Address { get; set; }
    };
    
    def test = Test("Dave Nemerle", 39,
        Address("100 Main Street", "Vancouver", "V4B 1B9"));
    print("test = $(test.name) $(test.address.City)\n");
    
    def serializer = Serializer();
    def _result = serializer.Serialize(System.Console.Out, test);
    
    def textin = @"---
    name: Bob F#
    age: 40
    address:
      street: 101 Commercial Drive
      city: Vancouver
      postcode: V6Z 3G6";
    
    def deserializer = Deserializer();
    def test2 = deserializer.Deserialize.[Test](StringReader(textin));
    def _result2 = serializer.Serialize(System.Console.Out, test2);
    

    [code]
    test = Dave Nemerle Vancouver
    name: Dave Nemerle
    age: 39
    address:
    street: 100 Main Street
    city: Vancouver
    postcode: V4B 1B9
    name: Bob F#
    age: 40
    address:
    street: 101 Commercial Drive
    city: Vancouver
    postcode: V6Z 3G6
    [/code]

    deserializer.Deserialize.[Test]() is Nemerle's way of saying deserializer.Deserialize<Test>(). It uses [] instead of <> for generic/template/type parameters, and the extra . in there is to prevent it from thinking that Deserialize is some kind of array-like property which needs indexing...
    Filed under: but not necessarily :wtf:


Log in to reply