Skip to content
rehanzo edited this page Aug 29, 2023 · 5 revisions

Focusing on differences, skipping aspects which are similar.

Type System

https://hush-shell.github.io/intro/type-system.html

As in Lua, Hush proposes only a handful of built-in types, and no user-defined types.

NGS:

  • provides many built in types.
  • supports user-defined types.
  • supports inheritance.
  • lacks the char type, most likely a mistake on my part

Basic Constructs

let pi = 3.14

NGS - pi = 3.14

String concatenation ++

NGS uses multiple dispatch. + is used for adding numbers and concatenation of strings.

Hush:

let array = [
    1 + 2,
    3.0 * std.float(4), # explicit conversion to float.
    21 % std.int(5.2), # explicit conversion to int.
    false and not true, # will short circuit.
    1 == 1.0, # false, int and float are always distinct.
    "abc" > "def",
    "hello" ++ " world",
]
std.assert(array[0] == 3)

NGS:

The commas at the end of line, separating the elements, are optional.

array = [
    1 + 2
    2.0 * Real(4)
    21 % Int(5.2)
    false and not(true)
    "abc" > "def"
    "hello" + " world"
]
assert(array[0] == 3)

Hush:

let dictionary = @[
    age: 20,
    height: 150,
    greet: function()
        std.print("hello!")
    end,
]

std.assert(dictionary["age"] == 20)
std.assert(dictionary.height == 150)
dictionary.greet() # prints "hello!"

NGS:

Optional commas at ends of lines. Quoted keys. Very direct translation. In NGS the following would be implemented with user defined type and F greet(x:MyType) ..., not a method in a dictionary.

assert() in NGS takes a pattern as the second argument.

dictionary = {
    'age': 20
    'height': 150
    'greet': { echo("hello!") }
}
assert(dictionary, {"age": 20, "height": 150})
dictionary::greet()

Control Flow and Functions

https://hush-shell.github.io/intro/control-flow.html

Functions are first class citizens

Same. Taken few steps forward though. Function defintion syntax is short, based on frequency of use:

  • F (...) ... - anonymous
  • F name(...) ... - multiple dispatch
  • { ... } - anonymous with 3 optional arguments: A, B and C.
  • my_func = some_func(X, 1, 2) - partially applied function, call as my_func(100).

They must declare how many arguments they expect, which is enforced when calling a function.

In NGS, parameters have types. These are also enforced when calling a function. Additionally, types serve multiple dispatch so:

Hush:

function takes_one(x)
    # ...
end

function takes_two(x, y)
    # ...
end

takes_one(1)
takes_two("a", 2)

NGS:

Sample, could be more like Hush but showing multiple dispatch here.

F takes(x) ...
F takes(x, y) ...

takes(1)
takes("a", 2)

Calling a function with less or more arguments than expected will result in a panic.

If no appropriate method found (among the candidate in multiple dispatch), an exception is thrown.

Hush provides one further facility for functions: the self keyword.

Nothing like this in NGS. There are types and methods. UFCS is supported, calling obj.meth(x) is exactly the same as meth(obj, x).

For loops

Hush:

for item in iter(array) do
    sum = sum + item
end

NGS:

Iter() is automatically called so for item in array { ... }

Error Handling

https://hush-shell.github.io/error-handling.html

Panic

NGS uses exceptions thoroughly.

Optional type Box/EmptyBox/FullBox and result type Result/Success/Failure are also supported.

safe_div_mod(5, 0)?

The quesion mark is not needed as NGS uses exceptions.

Shell capabilities

{ echo Hello world! }

NGS has multitude of mechanisms to run external commands and many controls

process_obj = $(...)
bg_process_obj = $(... &)
output = `...`
parsed_output = ``...`` (parses for example JSON)

Errors

Consistent with the rest of the language, NGS throws exception on errors.

Note that errors are not one-to-one with non-zero exit code. grep for example returns 1 if nothing is found. That's false in boolean context in NGS but it's not an exception.

For unknown commands, non-zero exit code is an exception but override is easy: $(ok:[0,1,4] my_command) - lists the exit codes which would not throw an exception.

Standard Library

https://hush-shell.github.io/std.html

std.args()

ARGV

std.assert(value)

https://ngs-lang.org/doc/latest/generated/assert.html

std.contains(collection, value)

std.env(key)

ENV.get(key)

std.is_empty(collection)

not(collection) - empty collection is false in boolean context

std.iter(collection)

Iter(collection)

std.json.dump(value)

value.encode_json() and higher level store("file_name.json", value)

std.json.parse(string)

string.decode_json() and higher level fetch("file_name.json")

std.regex(pattern)

/pattern/

std.to_string(value)

value.Str()

std.typecheck(value, type)

assert(value, type) - type must be the type object such as Arr, Hash, Bool, etc.

std.try_typecheck(value, type)

value is type - type must be the type object such as Arr, Hash, Bool, etc.

std.type(value)

typeof(value) - returns the type object

Paradigms

https://hush-shell.github.io/paradigms/index.html

NGS:

  • has more typing features
  • more ergonomuc functional programming
  • supports multiple dispatch which is heavilty used across the language

OOP

https://hush-shell.github.io/paradigms/oop.html

Hush:

# This function acts like a combination of a class and a constructor. It'll take any
# arguments relevant to the construction of a `MyCounter` object, and will return an
# instance of such object, which is nothing but a dictionary.
let MyCounter = function(start)
    @[
        # A member field. Using the same convention as Python, a field starting with an
        # underscore should be considered private.
        _start: start,

        # A method. Here, we can use the `self` keyword to access the object.
        get: function()
            self._start
        end,

        # Another method.
        increment: function()
            self._start = self._start + 1
        end,
     ]
end

let counter = MyCounter(1) # object instantiation.

std.print(counter.get()) # 1
counter.increment()
std.print(counter.get()) # 2

NGS:

type MyCounter

F init(mc:MyCounter, start:Int) mc._start = start

F get(mc:MyCounter) mc._start

F increment(mc:MyCounter) mc._start += 1

counter = MyCounter(1)
echo(counter.get())
counter.increment()
echo(counter.get())

Inheritance

Hush:

let MySuperCounter = function (start, step)
    # Instantiate an object of the base class. We'll then augment this object with
    # derived functionality.
    let counter = MyCounter(start)

    counter._step = step # Add new fields to the object.

    # Override a method. Make sure not to change the number of parameters here!
    counter.increment = function ()
        self._start = self._start + self._step
    end

    # In order to override a method and call the parent implementation, you'll need to
    # bind it to the current object, and then store it to a variable:
    let super_get = std.bind(counter, counter.get)
    counter.get = function()
        let value = super_get() # call the parent method.
        std.print(value)
        value
    end

    counter
end


let super_counter = MySuperCounter(2, 3)
super_counter.get() # 2
super_counter.increment()
super_counter.get() # 5

NGS:

type MySuperCounter(MyCounter)
echo(MySuperCounter)

F init(msc:MySuperCounter, start:Int, step:Int) {
    super(msc, start)
    msc._step = step
}
# Inherited: F get(...) ...

F increment(msc:MySuperCounter) msc._start += msc._step


# "super_counter" hits syntax bug, renamed to "sup_counter"
sup_counter = MySuperCounter(2, 3)
echo(sup_counter.get())
sup_counter.increment()
echo(sup_counter.get())

NGS, how I would really do it - use optional prameter step:

type MyCounter

F init(mc:MyCounter, start:Int, step:Int=1) {
    mc._start = start
    mc._step = step
}

F get(mc:MyCounter) mc._start

F increment(mc:MyCounter) mc._start += mc._step


counter = MyCounter(1, 3)
echo(counter.get())
counter.increment()
echo(counter.get())

Functional

https://hush-shell.github.io/paradigms/fun.html

(Maybe later)

Meanwhile, see Iter implementation for an array:

https://github.com/ngs-lang/ngs/blob/bdfb2fd70162cd7183ac8d45c774a5718cb19b09/lib/autoload/globals/Iter.ngs#L279-L318

Extras

Noticable features that NGS has and were not mentioned in Hush:

  • Automatic parsing of command line arguments (F main(...) ... in NGS)
  • Last statement in the program is converted to exit code
  • Facility for preparing command line arguments when running external programs - https://ngs-lang.org/doc/latest/generated/Argv.html
  • Common ops pieces of code factored out - log(), warn(), error(), retry()