fhatos logo FhatOS (pronounced fat-ahs) is a distributed operating system for ESP8266, ESP32, Raspberry PI, and similar fabrications. Moreover, sandboxed deployments on Linux and MacOSX systems offer the cluster large memory/storage space and processor speed. All fHATOS​ resources, from individual datum, complex data structures, files, and threads exist within a single URI address space called furi (pronounced "fury" or "fhat URI") — a subset of the common URI space. Programs are written in mm-ADT or C / C++ and communicate with one another via storage structures that maintain subsets of the fURI space. In general, fHATOS​ provides a convenient medium for coordinating a heterogeneous collection of hardware processors and their peripheries.

FhatOS Features

  1. A hardware-agnostic scheduler for executing multi-threaded monoids.

  2. A memory architecture enabling the integration of various storage mediums within a single URI address space.

  3. A distributed file system embedded in the URI address space.

  4. A programming language for fluently creating monoids to control a distributed swarm of monads.

  5. A REPL environment for writing and deploying monoids in real-time.

  6. A collection common embedded systems protocols GPIO, PWM, I2C, and SPI.

  7. A suite of common sensor, actuator, and UI modules.

  8. A sandboxed distribution enabling Linux and MacOS systems to participate in the cluster.

  9. A monoidal bootloader with support for OTA firmware updates.

FhatOS Boot Loader

The following output is from a Linux boot of FHAtOS​. The purpose of this documentation is to explain the mechanics of the boot process and beyond.

$ fhatos --boot:config=../conf/boot-loader.obj
            PhaseShift Studio Presents
 <`--'>____  ______ __  __  ______  ______  ______  ______
 /. .  `'  \/\  ___/\ \_\ \/\  __ \/\__  _\/\  __ \/\  ___\
(`')  ,     @ \  __\ \  __ \ \  __ \/_/\ \/\ \ \_\ \ \___  \
 `-._,     / \ \_\  \ \_\ \_\ \_\ \_\ \ \_\ \ \_____\/\_____\
    )-)_/-(>  \/_/   \/_/\/_/\/_/\/_/  \/_/  \/_____/\/_____/
                                   A Dogturd Stynx Production
    fhatos-0.1-alpha > linux-6.8.0-54-generic > x86_64
       [x86_64]
      Use noobj for noobj
      .oO loading system objs Oo.
[INFO]  [/sys/scheduler] scheduler started
[INFO]  [/sys/router] router started
[INFO]  [/sys/router] main memory [total=>5760]
[INFO]  [/sys/router] heap <none> spanning /sys/# mounted
[INFO]  [/sys/router] heap <none> spanning /mnt/# mounted
[INFO]  [/sys/router] heap /mnt/boot spanning /boot/# mounted
[INFO]  [/sys/router] ../../../conf/boot_config.obj boot config file loaded (size: 813 bytes)
[INFO]  [/sys/router]
  [
    router=>[resolve=>[namespace=>[:=>/mmadt/,fos:=>/fos/],auto_prefix=>[,/mmadt/,/mmadt/ext/,/fos/,/fos/sys/,/fos/io/,/fos/sensor/,/fos/ui/,/fos/util/,/sys/],query=>[write=>[lock=>to_do]],default_config=>[query=>[write=>[sub=>noobj]]]]]
    scheduler=>[def_stack_size=>8096]
    mqtt=>[broker=>mqtt://chibi.local:1883,client=>fhatos_client,async=>true,cache_size=>50]
    wifi=>[ssid=>Rodkins-2G,password=>'puppymama',mdns=>fhatos]
    ota=>[host=>mdns://fhatos_client:3232]
    console=>[terminal=>[stdout=>/io/terminal/:stdout,stdin=>/io/terminal/:stdin],nest=>2,ellipsis=>50,prompt=>'fhatos> ',strict=>false,log=>INFO,stack_size=>24288,stack_trace=>true]
    fs=>[root=>./data/fs]
  ]@/boot/config
[INFO]  [/sys/router] router boot config dropped
[INFO]  [/sys/router] scheduler boot config dropped
[INFO]  [/sys/router] /sys/lib/heap type imported
[INFO]  [/sys/router] /sys/lib/dsm type imported
[INFO]  [/sys/router] /sys/lib/bus type imported
[INFO]  [/sys/router] heap /mnt/fos spanning /fos/# mounted
      .oO loading mmadt lang Oo.
[INFO]  [/mnt/mmadt] query processor /mnt/mmadt/q/doc attached
[INFO]  [/sys/router] heap /mnt/mmadt spanning /mmadt/# mounted
      .oO loading fos models Oo.
[INFO]  [/sys/router] heap /mnt/io spanning /io/# mounted
[INFO]  [/sys/router] /io/parser obj loaded
[INFO]  [/io/log] switching from boot logger to system logger
[INFO]  [/sys/router] /io/log obj loaded
[INFO]  [/sys/router] log boot config dropped
[INFO]  [/sys/router] heap /mnt/cache spanning +/# mounted
[INFO]  [/sys/type] /sys/structure/lib/fs/:create type defined
[INFO]  [/sys/router] /io/lib/fs type imported
[INFO]  [/mnt/disk] /home/killswitch/software/fhatos/build/docs/build/data/fs file system location mounted
[INFO]  [/sys/router] fs /mnt/disk spanning /disk/# mounted
[INFO]  [/sys/router] fs boot config dropped
[INFO]  [/mnt/dsm] query processor /mnt/dsm/ attached
[INFO]  [/mmadt/rec] mqtt://chibi.local:1883 mqtt fhatos_client connected
[INFO]  [/sys/router] dsm /mnt/dsm spanning /shared/# mounted
[INFO]  [/sys/router] mqtt boot config dropped
[INFO]  [/mnt/bus] mapping /bus==>//io
[INFO]  [/sys/router] bus /mnt/bus spanning /bus/# mounted
[INFO]  [/sys/router] /io/console obj loaded
[INFO]  [/io/console] thread spawned: inst()[cpp]
[INFO]  [/sys/router] console boot config dropped

Booting on Linux/Unix/Mac

Booting on ESP32

Booting on ESP8266

Booting on RaspberryPi

FhatOS Architecture

The "animal sticker" images used throughout the documentation are of the chickens, ducks, dogs and cats that have or are currently living on the FhatFarm. To learn their names, hover on their image.

cooties fhatOS​ is designed according to the philosophy that computing is composed of 3 fundamental, interacting phenomena: structure (space), process (time), and language (perspective). As such,the fHatOS​ kernel is comprised of the followng resources:

  1. /sys/scheduler (process): coordinates all processes realized as threads, fibers, and coroutines.

  2. /sys/router (structure) : manages all structures comprising a distributed, partitioned, read/write tuple space.

  3. /mmadt/ (language): provides parsing, type reasoning, and execution of mmADT programs.

These resources are accessible via their respective fURIs. The fURI space is a subset of the common URI space, and is the address space through which all resources within fhatos​ communicate. A fURI is dereferenced using the mmADT from instruction (sugar’d *). Dereferencing returns the resources pointed to by the fURI. In mmADT, these resources are called obj (objects).

  • /sys/scheduler

  • /sys/router

  • /mmadt

The scheduler controls and provides access to the various processes that define the Fhatos​ process architecture.

fhatos> */sys/scheduler
>[
==>config=>[
===>def_stack_size=>8096
=>]
==>thread=>[
=>]
==>::=>[
===>spawn=>spawn?obj<=obj{?}(_)[cpp]
=>]
>]@/sys/scheduler

The router is responsible for storing and retrieving objs from a pool of structures that define the FHaTOS​ memory architecture.

fhatos> */sys/router
>[
==>frame=>[rec][_]
==>config=>[
===>resolve=>[
====>namespace=>[
=====>:=>/mmadt/
=====>fos:=>/fos/
===>]
====>auto_prefix=>[
=====>
=====>/mmadt/
=====>/mmadt/ext/
=====>/fos/
=====>/fos/sys/
=====>/fos/io/
=====>/fos/sensor/
=====>/fos/ui/
=====>/fos/util/
=====>/sys/
===>]
====>query=>[
=====>write=>[
======>lock=>to_do
====>]
===>]
====>default_config=>[
=====>query=>[
======>write=>[sub=>noobj]
====>]
===>]
==>]
=>]
==>query=>[
===>write=>[
====>lock=>lock?obj{?}<=obj{?}()[cpp]
====>sub=>sub?obj{?}<=obj()[cpp]
==>]
=>]
==>structure=>[
===>/sys/#
===>/mnt/#
===>/boot/#
===>/fos/#
===>/mmadt/#
===>/io/#
===>+/#
===>/disk/#
===>/shared/#
===>/bus/#
=>]
>]@/sys/router

The mmADT language is embedded in the fURI address space thus enabling reflective programming.

fhatos> */mmadt/#/
>[
==>/mmadt/as=>as(type?uri=>_)[cpp]
==>/mmadt/at=>at?obj{?}<=obj{?}(isa(/mmadt/uri))[cpp]
==>/mmadt/barrier=>barrier?objs{*}<=objs{*}(_)[cpp]
==>/mmadt/bcode=>[bcode][_]
==>/mmadt/bcode/::/mmadt/inspect=>inspect(_)[cpp]
==>/mmadt/block=>block?obj<=obj{?}(_)[cpp]
==>/mmadt/bool=>[bool][_]
==>/mmadt/bool/::/mmadt/as=>as(isa(/mmadt/uri))[cpp]
==>/mmadt/bool/::/mmadt/div=>div(isa(/mmadt/bool))[cpp]
==>/mmadt/bool/::/mmadt/inspect=>inspect(_)[cpp]
==>/mmadt/bool/::/mmadt/minus=>minus(isa(/mmadt/bool))[cpp]
==>/mmadt/bool/::/mmadt/mult=>mult(isa(/mmadt/bool))[cpp]
==>/mmadt/bool/::/mmadt/neg=>neg(isa(/mmadt/bool))[cpp]
==>/mmadt/bool/::/mmadt/plus=>plus(isa(/mmadt/bool))[cpp]
==>/mmadt/chain=>chain(_)[cpp]
==>/mmadt/choose=>choose(_)[cpp]
==>/mmadt/count=>count?int<=objs{*}()[cpp]
==>/mmadt/div=>div(_)
==>/mmadt/drop=>drop?obj{?}<=obj{?}(isa(/mmadt/obj))[cpp]
==>/mmadt/each=>each(isa(/mmadt/obj))
==>/mmadt/else=>else?obj<=obj{?}(_)[cpp]
==>/mmadt/embed=>embed()[cpp]
==>/mmadt/end=>end?noobj{.}<=obj{*}()[cpp]
==>/mmadt/eq=>eq(_)[cpp]
==>/mmadt/error=>[error][_]
==>/mmadt/explain=>explain()[cpp]
==>/mmadt/ext/C=>C()[is(gte(-273.14999))]
==>/mmadt/ext/Ox=>Ox()[is(true)]
==>/mmadt/ext/char=>char()[merge(2).count().is(eq(1))]
==>/mmadt/ext/int16=>[int16][_]
==>/mmadt/ext/int32=>[int32][_]
==>/mmadt/ext/int8=>uint8()[is(gte(-127)).is(lte(128))]
==>/mmadt/ext/ms=>[real][_]
==>/mmadt/ext/ms/::/mmadt/as=>as(is(eq(/mmadt/ext/sec)))[cpp]
==>/mmadt/ext/nat=>nat()[is(gte(0))]
==>/mmadt/ext/prnt=>prnt()[is(gte(0.00000)).is(lte(100.00000))]
==>/mmadt/ext/sec=>[real][_]
==>/mmadt/ext/secret=>[str][_]
==>/mmadt/ext/secret/::/mmadt/as=>as(from(0?type,noobj)[cpp])[cpp]
==>/mmadt/ext/uint8=>uint8()[is(gte(0)).is(lte(255))]
==>/mmadt/flip=>flip(_)[cpp]
==>/mmadt/frame=>frame?rec<=obj{?}()[cpp]
==>/mmadt/from=>from?obj{?}<=obj{?}(_,else(noobj))[cpp]
==>/mmadt/gt=>gt()
==>/mmadt/gte=>gte()
==>/mmadt/inspect=>inspect(_)
==>/mmadt/inst=>[inst][_]
==>/mmadt/inst/::/mmadt/inspect=>inspect(_)[cpp]
==>/mmadt/inst/blockers=>[
===>block
===>each
===>within
===>isa
===>split
===>choose
===>chain
=>]
==>/mmadt/int=>[int][_]
==>/mmadt/int/::/mmadt/as=>as(isa(/mmadt/uri))[cpp]
==>/mmadt/int/::/mmadt/div=>div(0?int=>isa(/mmadt/int))[cpp]
==>/mmadt/int/::/mmadt/gt=>gt(_)[cpp]
==>/mmadt/int/::/mmadt/gte=>gte(_)[cpp]
==>/mmadt/int/::/mmadt/inspect=>inspect(_)[cpp]
==>/mmadt/int/::/mmadt/lt=>lt(_)[cpp]
==>/mmadt/int/::/mmadt/lte=>lte(_)[cpp]
==>/mmadt/int/::/mmadt/minus=>minus(0?int=>isa(/mmadt/int))[cpp]
==>/mmadt/int/::/mmadt/mod=>mod(isa(/mmadt/int))[cpp]
==>/mmadt/int/::/mmadt/mult=>mult(0?int=>isa(/mmadt/int))[cpp]
==>/mmadt/int/::/mmadt/neg=>neg(isa(/mmadt/int))[cpp]
==>/mmadt/int/::/mmadt/plus=>plus(0?int=>isa(/mmadt/int))[cpp]
==>/mmadt/is=>is?obj{?}<=obj(_)[cpp]
==>/mmadt/isa=>isa?obj{?}<=obj{?}(_)[cpp]
==>/mmadt/lift=>lift(_)[cpp]
==>/mmadt/lock=>lock(user=>_)[cpp]
==>/mmadt/lshift=>lshift()
==>/mmadt/lst=>[lst][_]
==>/mmadt/lst/::/mmadt/div=>div(isa(/mmadt/lst))[cpp]
==>/mmadt/lst/::/mmadt/each=>each(isa(/mmadt/lst))[cpp]
==>/mmadt/lst/::/mmadt/inspect=>inspect(_)[cpp]
==>/mmadt/lst/::/mmadt/merge=>merge?objs{*}<=lst()[cpp]
==>/mmadt/lst/::/mmadt/minus=>minus(isa(/mmadt/lst))[cpp]
==>/mmadt/lst/::/mmadt/mult=>mult(isa(/mmadt/lst))[cpp]
==>/mmadt/lst/::/mmadt/plus=>plus(isa(/mmadt/lst))[cpp]
==>/mmadt/lst/::/mmadt/split=>split(isa(/mmadt/lst))[cpp]
==>/mmadt/lst/::/mmadt/within=>within(_)[cpp]
==>/mmadt/lt=>lt()
==>/mmadt/lte=>lte()
==>/mmadt/map=>map?obj{?}<=obj{?}(_)[cpp]
==>/mmadt/merge=>merge?obj{?}<=obj()[cpp]
==>/mmadt/minus=>minus(_)
==>/mmadt/mod=>mod(from(0?rhs,noobj)[cpp])
==>/mmadt/mult=>mult(_)
==>/mmadt/neg=>neg(isa(/mmadt/obj))
==>/mmadt/neq=>neq(_)[cpp]
==>/mmadt/noobj=>[noobj][_]
==>/mmadt/not=>not?obj{?}<=obj(_)[cpp]
==>/mmadt/obj=>[obj][_]
==>/mmadt/objs=>[objs][_]
==>/mmadt/plus=>plus(_)
==>/mmadt/print=>print?obj{?}<=obj{?}(_)[cpp]
==>/mmadt/prod=>prod?obj<=objs{*}()[cpp]
==>/mmadt/real=>[real][_]
==>/mmadt/real/::/mmadt/as=>as(isa(/mmadt/uri))[cpp]
==>/mmadt/real/::/mmadt/div=>div(isa(/mmadt/real))[cpp]
==>/mmadt/real/::/mmadt/gt=>gt(_)[cpp]
==>/mmadt/real/::/mmadt/gte=>gte(_)[cpp]
==>/mmadt/real/::/mmadt/inspect=>inspect(_)[cpp]
==>/mmadt/real/::/mmadt/lt=>lt(_)[cpp]
==>/mmadt/real/::/mmadt/lte=>lte(_)[cpp]
==>/mmadt/real/::/mmadt/minus=>minus(isa(/mmadt/real))[cpp]
==>/mmadt/real/::/mmadt/mult=>mult(isa(/mmadt/real))[cpp]
==>/mmadt/real/::/mmadt/neg=>neg(isa(/mmadt/real))[cpp]
==>/mmadt/real/::/mmadt/plus=>plus(isa(/mmadt/real))[cpp]
==>/mmadt/rec=>[rec][_]
==>/mmadt/rec/::/mmadt/div=>div(isa(/mmadt/rec))[cpp]
==>/mmadt/rec/::/mmadt/each=>each(isa(/mmadt/rec))[cpp]
==>/mmadt/rec/::/mmadt/inspect=>inspect(_)[cpp]
==>/mmadt/rec/::/mmadt/lshift=>lshift(isa(/mmadt/int))[cpp]
==>/mmadt/rec/::/mmadt/merge=>merge?objs{*}<=rec()[cpp]
==>/mmadt/rec/::/mmadt/minus=>minus(isa(/mmadt/rec))[cpp]
==>/mmadt/rec/::/mmadt/mult=>mult(isa(/mmadt/rec))[cpp]
==>/mmadt/rec/::/mmadt/plus=>plus(isa(/mmadt/rec))[cpp]
==>/mmadt/rec/::/mmadt/rshift=>rshift(isa(/mmadt/uri))[cpp]
==>/mmadt/rec/::/mmadt/within=>within(_)[cpp]
==>/mmadt/ref=>ref?obj{?}<=obj{?}(_,block(noobj))[cpp]
==>/mmadt/repeat=>repeat(from(0?code,noobj)[cpp],from(1?until,true)[cpp],from(2?emit,false)[cpp])[cpp]
==>/mmadt/rshift=>rshift()
==>/mmadt/split=>split(_)[cpp]
==>/mmadt/start=>start?objs{*}<=noobj{.}(_)[cpp]
==>/mmadt/str=>[str][_]
==>/mmadt/str/::/mmadt/as=>as(isa(/mmadt/uri))[cpp]
==>/mmadt/str/::/mmadt/div=>div(isa(/mmadt/str))[cpp]
==>/mmadt/str/::/mmadt/gt=>gt(_)[cpp]
==>/mmadt/str/::/mmadt/gte=>gte(_)[cpp]
==>/mmadt/str/::/mmadt/inspect=>inspect(_)[cpp]
==>/mmadt/str/::/mmadt/lt=>lt(_)[cpp]
==>/mmadt/str/::/mmadt/lte=>lte(_)[cpp]
==>/mmadt/str/::/mmadt/merge=>merge?objs{*}<=str()[cpp]
==>/mmadt/str/::/mmadt/minus=>minus(isa(/mmadt/str))[cpp]
==>/mmadt/str/::/mmadt/mult=>mult(isa(/mmadt/str))[cpp]
==>/mmadt/str/::/mmadt/plus=>plus(isa(/mmadt/str))[cpp]
==>/mmadt/str/::/mmadt/within=>within(_)[cpp]
==>/mmadt/sum=>sum?obj<=objs{*}()[cpp]
==>/mmadt/to=>to(_,true)[cpp]
==>/mmadt/type=>type?uri<=obj{?}(_)[cpp]
==>/mmadt/uri=>[uri][_]
==>/mmadt/uri/::/mmadt/as=>as(isa(/mmadt/uri))[cpp]
==>/mmadt/uri/::/mmadt/div=>div(isa(/mmadt/uri))[cpp]
==>/mmadt/uri/::/mmadt/gt=>gt(_)[cpp]
==>/mmadt/uri/::/mmadt/gte=>gte(_)[cpp]
==>/mmadt/uri/::/mmadt/inspect=>inspect(_)[cpp]
==>/mmadt/uri/::/mmadt/lshift=>lshift(_)[cpp]
==>/mmadt/uri/::/mmadt/lt=>lt(_)[cpp]
==>/mmadt/uri/::/mmadt/lte=>lte(_)[cpp]
==>/mmadt/uri/::/mmadt/merge=>merge?objs{*}<=uri()[cpp]
==>/mmadt/uri/::/mmadt/minus=>minus(isa(/mmadt/uri))[cpp]
==>/mmadt/uri/::/mmadt/mult=>mult(isa(/mmadt/uri))[cpp]
==>/mmadt/uri/::/mmadt/plus=>plus(isa(/mmadt/uri))[cpp]
==>/mmadt/uri/::/mmadt/rshift=>rshift(_)[cpp]
==>/mmadt/within=>within(_)
>]
The [cpp] representation of an inst value means that the instruction’s implementation is written C++. As such, no further introspection is possible from within mmADT. When the instruction implementation is written in mmADT, the instruction value is displayed as bcode (a linear chain of objs).

An inst written in C++.

fhatos> *int::lt
==>lt?bool<=int(_)[cpp]

An inst written in mmADT.

fhatos> *nat
==>nat?int<=int()[is(gte(0))]

A fhATOs​ instance is shutdown by writing noobj (null) to every fURI address.

fhatos> # -> noobj
[ERROR] [/sys/router] # crosses multiple structures
[INFO]  [/sys/router] 1 bus(s) closing
[INFO]  [/sys/router] 1 dsm(s) closing
[INFO]  [/sys/router] 1 fs(s) closing
[INFO]  [/sys/router] 7 heap(s) closing
[INFO]  [/mmadt/rec] disconnecting from [mqtt://chibi.local:1883]
[INFO]  [/sys/router] router /sys/router stopped
[INFO]  [/sys/router] /sys/# heap detached
[INFO]  [/sys/router] /mnt/# heap detached
[INFO]  [/sys/router] /boot/# heap detached
[INFO]  [/sys/router] /fos/# heap detached
[INFO]  [/sys/router] /mmadt/# heap detached
[INFO]  [/sys/router] /io/# heap detached
[INFO]  [/sys/router] +/# heap detached
[INFO]  [/sys/router] /disk/# fs detached
[INFO]  [/sys/router] /shared/# dsm detached
[INFO]  [/sys/router] /bus/# bus detached

This documentation will explore these three kernel resources in-depth starting with the mmADT language and processor.

The mm-ADT Language

punky mmADT is the programming language of FHaTOs​. In mmADT, every expression is an obj (object). The language has an underlying monoidal structure where an obj can be applied (.) to an obj to create an obj.

mmadt monoid

⠀⠀ An obj is composed of a type, a value, a variable frame, and a storage location/reference. The abstract syntax of a sugar-free obj is

\[\tt{obj} := \tt{type}(\tt{frame})[\tt{value}]@\tt{ref}\]
  1. The type is a fURI referring to an obj which determines whether the obj is of that type or not (predicate).

  2. The frame is a collection of fURI referenced objs that are accessible to the value of the obj (arguments).

  3. The value is a collection of objs denoting the form of the obj (encoding).

  4. The reference is a fURI denoting the durable location of the obj with the underlying storage structure (memory address).

The mmADT language and its evaluation by a processor will be explained via an exploration of these substructures, where finer grained structures lie within each.

The Type

\[\tt{obj} := \color{yellow}{\underline{\tt{type}}}(\tt{frame})[\tt{value}]@\tt{ref}\]

There are 9 base types in mmADT. 6 mono-types and 3 poly-types. The mono-types are:

  1. /mmadt/noobj: A singleton representing null.

  2. /mmadt/bool: The set of binary values true and false.

  3. /mmadt/int: The set of \$n\$-bit integers between \$-2^(n-1)\$ and \$2^(n-1)\$.

  4. /mmadt/real: The set of \$n\$-bit floating point values between -…​ and …​..

  5. /mmadt/str: The infinite set of all UTF-8 character sequences.

  6. /mmadt/uri: The infinite set of all fHATos​ UTF-8 Uniform Resource Identifiers (fURIs).

The poly-types are:

  1. /mmadt/lst: An (un)ordered collection of zero or more objs.

  2. /mmadt/rec: An (un)ordered collection of key/value pair objs, where keys are unique.

All other types are defined in terms of these types. Every obj has an explicitly declared type. However, given frequency of base types usage, specifying the type is not necessary it can be deduced from the value.

  • bool

  • int

  • real

  • str

  • uri

  • lst

  • rec

  • noobj

fhatos> /mmadt/bool[true]
==>true
fhatos> bool[true]
==>true
fhatos> true
==>true
fhatos> /mmadt/int[6]
==>6
fhatos> int[6]
==>6
fhatos> 6
==>6
fhatos> /mmadt/real[6.2]
==>6.200000
fhatos> real[6.2]
==>6.200000
fhatos> 6.2
==>6.200000
fhatos> /mmadt/str['cooties']
==>'cooties'
fhatos> str['cooties']
==>'cooties'
fhatos> 'cooties'
==>'cooties'
fhatos> /mmadt/uri[/dog/curly]
==>/dog/curly
fhatos> uri[/dog/curly]
==>/dog/curly
fhatos> /dog/curly
==>/dog/curly
fhatos> /mmadt/lst[['a',2,true]]
==>['a',2,true]
fhatos> lst[['a',2,true]]
==>['a',2,true]
fhatos> ['a',2,true]
==>['a',2,true]
fhatos> /mmadt/rec[[a=>6,b=>false]]
==>[a=>6,b=>false]
fhatos> rec[[a=>6,b=>false]]
==>[a=>6,b=>false]
fhatos> [a=>6,b=>false]
==>[a=>6,b=>false]
fhatos> /mmadt/noobj[]
fhatos> noobj[]
fhatos> noobj
fhatos>

fhatos>

When an mmADT obj is wrapped in a type[]-bracket, the type fURI is first resolved to it’s obj form (typically as an inst) and then the wrapped obj is applied to it. If the result of the application yields an error or a noobj, then the base value obj is not of that type and a type error is thrown. However, should any other obj be returned, then the base value obj is of that type and is returned wrapped in the respective type[]-bracket.

\[ \begin{align*} \tt{type}_\tt{furi}[\tt{obj}] &= \; \\ \tt{type}_\tt{furi}[\tt{obj}] &= \; ^*\tt{type}_\tt{furi} \cdot \tt{obj} \\ \tt{type}_\tt{furi}[\tt{obj}] &= \; \tt{type}_\tt{obj} \cdot \tt{obj} \\ \tt{type}_\tt{furi}[\tt{obj}] &= \; \left\{ \begin{array}{lr} \tt{error} & \text{if} \; \tt{type}_\tt{obj}(\tt{obj}) \in \{\tt{error}, \tt{noobj}\} \\ \tt{type}_\tt{furi}[\tt{obj}] & \text{otherwise}. \end{array}\right\} \end{align*} \]

The type can be understood as a predicate, where an error or noobj is false, otherwise true. Finally, if the obj has a @-reference, then any subsequent mutations to that obj must continue to satisfy the constraints of the type. If any mutation falls outside the bounds of the type, a type error is thrown. The @-reference ensures that as the referenced obj mutates, it’s corresponding representation in the underlying fURI structure mutates as well. This captures the notion of pass-by-reference vs. pass-by-value.

The mechanics of obj typing are exemplified below using the generally useful types provided by the /mmadt/ext prefix.

fhatos> */mmadt/ext/#/
>[
==>/mmadt/ext/C=>C()[is(gte(-273.14999))]
==>/mmadt/ext/Ox=>Ox()[is(true)]
==>/mmadt/ext/char=>char()[merge(2).count().is(eq(1))]
==>/mmadt/ext/int16=>[int16][_]
==>/mmadt/ext/int32=>[int32][_]
==>/mmadt/ext/int8=>uint8()[is(gte(-127)).is(lte(128))]
==>/mmadt/ext/ms=>[real][_]
==>/mmadt/ext/ms/::/mmadt/as=>as(is(eq(/mmadt/ext/sec)))[cpp]
==>/mmadt/ext/nat=>nat()[is(gte(0))]
==>/mmadt/ext/prnt=>prnt()[is(gte(0.00000)).is(lte(100.00000))]
==>/mmadt/ext/sec=>[real][_]
==>/mmadt/ext/secret=>[str][_]
==>/mmadt/ext/secret/::/mmadt/as=>as(from(0?type,noobj)[cpp])[cpp]
==>/mmadt/ext/uint8=>uint8()[is(gte(0)).is(lte(255))]
>]
  • char

  • nat

  • celsius

A char is a str containing a single character.

fhatos> *char
==>char?int<=str()
	[merge(2).count().is(eq(1))]
fhatos> char['a']@a
==>char['a']@a
fhatos> char['b']@b
==>char['b']@b
fhatos> @a + @b
[ERROR] [/sys/scheduler] [/mmadt/noobj] noobj accessed as str
	  thrown at inst char['a']@a => plus()[cpp] [0=>'@a + @b',code=>'@a + @b']
fhatos> *a
==>char['a']@a
fhatos> @a.as(str) + @b
==>'aa'@a

A natural number is an element of the set \(\mathbb{N} = \{0,1,2,\ldots,\infty\}\).

fhatos> *nat
==>nat?int<=int()[is(gte(0))]
fhatos> nat[12]
==>nat[12]
fhatos> nat[-30]
[ERROR] [/sys/scheduler] [/sys/lang/parser] -30 is not a /mmadt/ext/nat as defined by nat()[is(gte(0))]
fhatos> nat[12]@a
==>nat[12]@a
fhatos> @a.minus(11)
==>nat[1]@a
fhatos> @a.minus(2)
[ERROR] [/sys/scheduler] [/sys/lang/parser] -1@a is not a /mmadt/ext/nat as defined by nat()[is(gte(0))]
	  thrown at inst nat[1]@a => minus(0?int=>2)[cpp] [0=>'@a.minus(2)',0?int=>2]
fhatos> *a
==>nat[1]@a

Celsius is a temperature metric ranging from absolute zero (-273.15°) to infinity.

fhatos> *C
==>C?real<=real()[is(gte(-273.14999))]
fhatos> C[0.0]
==>C[0.000000]
fhatos> C[274.0]
==>C[274.000000]
fhatos> C[-274.0]
[ERROR] [/sys/scheduler] [/sys/lang/parser] -274.00000 is not a /mmadt/ext/C as defined by C()[is(gte(-273.14999))]

The Frame

\[\tt{obj} := \tt{type}(\color{yellow}{\underline{\tt{frame}}})[\tt{value}]@\tt{ref}\]

The frame of an obj is a set of fURI named variables that are dereferenceably accessible within the value of the obj. If an obj has a specified frame, the obj is called an inst (instruction). An inst is a function

\[f(\tt{obj}_\tt{in},a_1,a_2,\ldots,a_n) \mapsto \tt{obj}_\tt{out},\]

where \(\tt{obj}_\tt{in}\) is the left hand side obj (input) and \(a_m\) are the variables of the obj frame (arguments). To demonstrate how frames work, the inst band is defined. This function takes two int arguments. If the incoming obj is within the bounds of the two ints, it is emitted, else noobj is returned. The arguments are stored in an inst-specific frame mounted in the router. When the inst completes it’s execution, the frame is unmounted. If an inst calls another inst, then a stack of frames is realized and arguments declared in the parent inst are accessible in the child inst.

  • positional args

  • named args

  • default args

  • typed args

  • contextual args

  • dependent args

  • refined type

\[\tt{obj}_\tt{in}.f(\color{yellow}{\_}, \color{yellow}{\_},\ldots,\__n) \mapsto \tt{obj}_\tt{out}\]

An inst frame is defined by the inst arguments. These arguments can be accessed within the inst via their lst position as a fURI. For example, <0> references the first argument, <1> the second argument, so on and so forth.

fhatos> band -> ||band?int{?}<=int(_,_)[is(gte(*<0>)).is(lte(*<1>))]
==>band?int{?}<=int(_,_)
	[band?int{?}<=int[is(gte(from(0))).is(lte(from(1)))]]
fhatos> 3.band(2,8)                           (1)
[ERROR] [/sys/scheduler] [/mmadt/bcode] from(0) accessed as int
	  thrown at inst 3 => gte(from(0)) [0=>'3.band(2,8)                           // <1>',1=>8]
	  thrown at inst 3 => band?int{?}<=int(2,8)[band?int{?}<=int[is(gte(from(0))).is(lte(from(1)))]] [0=>'3.band(2,8)                           // <1>',1=>8]
fhatos> 10.band(2,8)                          (3)
[ERROR] [/sys/scheduler] [/mmadt/bcode] from(0) accessed as int
	  thrown at inst 10 => gte(from(0)) [0=>'10.band(2,8)                          // <3>',1=>8]
	  thrown at inst 10 => band?int{?}<=int(2,8)[band?int{?}<=int[is(gte(from(0))).is(lte(from(1)))]] [0=>'10.band(2,8)                          // <3>',1=>8]
1 …​
2 …​
\[\tt{obj}_\tt{in}.f(\color{yellow}{a_1} \Rightarrow x, \color{yellow}{a_2} \Rightarrow y,\ldots,a_n) \mapsto \tt{obj}_\tt{out}\]

A named argument denotes a fURI that references an obj in the router’s frame structure.

fhatos> band -> ||band?int{?}<=int(min=>_,max=>_)[is(gte(*min)).is(lte(*max))]
==>band?int{?}<=int(min=>_,max=>_)
	[band?int{?}<=int[is(gte(from(min))).is(lte(from(max)))]]
fhatos> 3.band(2,8)                           (1)
==>3
fhatos> 3.band(max=>8,min=>2)                 (2)
==>3
fhatos> 10.band(2,max=>8)                     (3)
==>band
fhatos> 'abc'.band(2,8)                       (4)
[ERROR] [/sys/scheduler] [/mmadt/int] 2 accessed as str
	  thrown at inst 'abc' => gte(from(min)) [0=>'\'abc\'.band(2,8)                       // <4>',1=>8,min=>2,max=>8]
	  thrown at inst 'abc' => band?int{?}<=int(2,8,min=>2,max=>8)[band?int{?}<=int[is(gte(from(min))).is(lte(from(max)))]] [0=>'\'abc\'.band(2,8)                       // <4>',1=>8,min=>2,max=>8]
1 If the argument name isn’t provided, then it’s position in the argument list determines its nane,
2 When arguments are named, they can be written in any order.
3 Named arguments and unnamed arguments can be used together.
4 The domain of band is int. Note where the error is thrown when str['abc'] is provided.
\[\tt{obj}_\tt{in}.f(a_1 \Rightarrow \color{yellow}{else(x)},a_2 \Rightarrow \color{yellow}{else(y)},\ldots,a_n) \mapsto \tt{obj}_\tt{out}\]

If the incoming obj to else is noobj, the else emits it’s argument, else it emits the incoming obj. This makes else useful for expressing default arguments.

fhatos> band -> ||band?int{?}<=int(min=>else(2),max=>else(8))[is(gte(*min)).is(lte(*max))]
==>band?int{?}<=int(min=>else?noobj<=obj(2)[noobj],max=>else?noobj<=obj(8)[noobj])
	[band?int{?}<=int[is(gte(from(min))).is(lte(from(max)))]]
fhatos> 1.band(min=>1)                        (1)
==>1
fhatos> 10.band()                             (2)
==>10
fhatos> {2,3,4,5}.band(min=>3,max=>4)         (3)
[ERROR] [/sys/scheduler] [/mmadt/obj] /mmadt/lte?dom=/mmadt/obj&dc=1,1&rng=/mmadt/bool&rc=1,1 inst unresolved
	         lhs id      inst id       resolve obj
	  ->[/mmadt/obj] /mmadt/lte => lte()
	  thrown at inst 2 => band?int{?}<=int(min=>3,max=>4)[band?int{?}<=int[is(gte(from(min))).is(lte(from(max)))]] [0=>'{2,3,4,5}.band(min=>3,max=>4)         // <3>',min=>3,max=>4]
1 No max is provided, so the default value of 8 is used.
2 No min nor max is provided, so the defaults 2 and 8, respectively are used.
3 When arguments are provided, the default values are not used.
\[\tt{obj}_\tt{in}.f(a_1\color{yellow}{?type_1}, a_2\color{yellow}{?type_2},\ldots,a_n) \mapsto \tt{obj}_\tt{out}\]

If an argument is defined with a query type, then the argument’s value must satisfy that type’s specification. This is a consequence of attaching a type query processor to the router frame structure and thus, is analogous to type specifications in other structures.

fhatos> is_divisible -> ||is_divisble?bool<=int(by?int=>_)[mod(*by).eq(0)]
==>is_divisble?bool<=int(by?int=>_)
	[is_divisble[mod(from(by)).eq(0)]]
fhatos> 10.is_divisible(2)
==>true
fhatos> 10.is_divisible('abc')
[ERROR] [/sys/scheduler] [/sys/lang/parser] 'abc' is not a int as defined by [int][_]
		 \_/mmadt/str is not a subtype of /mmadt/int
fhatos> is_divisible -> ||is_divisble?bool<=int(by?int=>_)[mod(*by).eq(0)]
==>is_divisble?bool<=int(by?int=>_)
	[is_divisble[mod(from(by)).eq(0)]]
fhatos> 10.is_divisible(2)
==>true
fhatos> 10.is_divisible('abc')
[ERROR] [/sys/scheduler] [/sys/lang/parser] 'abc' is not a int as defined by [int][_]
		 \_/mmadt/str is not a subtype of /mmadt/int
\[\tt{obj}_\tt{in}.f(a_1 \Rightarrow x,a_2 \Rightarrow \color{yellow}{g(*a_1)},\ldots,a_n) \mapsto \tt{obj}_\tt{out}\]

If an argument’s type is dependent on the value of another argument, then the argument is a dependent argument.

fhatos> band -> ||band?int{?}<=int(min=>_,max=>is(gt(*min)).else(*min.plus(1)))[is(gte(*min)).is(lte(*max))]
==>band?int{?}<=int(min=>_,max=>is?noobj<=obj(gt?noobj<=obj(from?noobj<=obj(min)[noobj])[noobj])[noobj].else?noobj<=obj(from?noobj<=obj(min)[noobj].plus?noobj<=obj(1)[noobj])[noobj...
	[band?int{?}<=int[is(gte(from(min))).is(lte(from(max)))]]
fhatos> 2.band(min=>1,max=>1)                 (1)
==>2
1 …​
fhatos> is_divisible -> ||is_divisble?bool<=int(by?int=>is(neq(0)).else(print('error').map(1)))[mod(*by).eq(0)]
==>is_divisble?bool<=int(by?int=>is?noobj<=obj(neq?noobj<=obj(0)[noobj])[noobj].else?noobj<=obj(print?noobj<=obj('error')[noobj].map?noobj<=...
	[is_divisble[mod(from(by)).eq(0)]]
fhatos> 10.is_divisible(2)
error==>true
fhatos> 10.is_divisible('abc')
error[ERROR] [/sys/scheduler] [/sys/lang/parser] 'abc' is not a int as defined by [int][_]
		 \_/mmadt/str is not a subtype of /mmadt/int

For instance:

fhatos> int(a=>2)[*a]
==>2
fhatos> 4 + int(a=>2)[*a]
==>6
fhatos> 4 + int(a=>2)[+*a]
==>10

The Value

\[\tt{obj} := \tt{type}(\tt{frame})[\color{yellow}{\underline{\tt{value}}}]@\tt{ref}\]

The value of an obj is the datum specifying the instance aspects of the obj within the boundaries of the type aspects of the obj.

The Reference

\[\tt{obj} := \tt{type}(\tt{frame})[\tt{value}]@\color{yellow}{\underline{\tt{ref}}}\]

The reference of an obj is a fURI denoting the location of the obj within the underlying fURI addressable structure. The FhaTOS​ structure is the storage medium of all persistent objs. If an obj does not have a reference, then the obj is transient — existing only within the data flow. When an obj has a reference, the obj encoding in the data flow (hardware main memory) and within the structure (FHAtOs​ persistence) are synchronized.

  • from *

  • at @

  • pubsub ?sub

fhatos> *y
fhatos> *z
fhatos> z -> 12
==>12
fhatos> y -> z
==>z
fhatos> *y
==>z
fhatos> **y
==>12
   [■]                         [■]
  ╱   ╲                       ╱   ╲
[■]    [■]  ┌*y┐           [■]    [■]
      ╱   ╲ ⮟  │                 ╱   ╲
   [■]     [z]@y ── **y ──────⮞[12]@z [■]
            │        ││         ⮝
            └─────── *z ────────┘

The fURI z references the int 12. The fURI y references the uri z. Dereferencing y yields z. A double dereference (i.e. **) of y jumps the monad from y to 12 as

\[ \begin{align*} *\tt{y} & \rightarrow \tt{z} \\ *\tt{z} & \rightarrow 12 \\ **\tt{y} & \rightarrow 12 \end{align*} \]
fhatos> a -> 12
==>12
fhatos> *a.plus(10)
==>22
fhatos> *a
==>12
fhatos> @a
==>12@a
fhatos> @a.plus(10)
==>22@a
fhatos> *a
==>22@a
   [■]                          [■]
  ╱   ╲                        ╱   ╲
[■]    [■]                   [■]    [■]
      ╱   ╲                            ╲
   [■]     [12]@a ──── @a.plus(10) ────⮞[22]@a

12 is written to a. 10 is added to a (pass by value *). a still stores 12. 10 is added to a (pass by reference @). a now stores 22.

fhatos> a?sub -> |to(b)
==>a?sub
fhatos> *a?sub
>sub[
==>source=>/io/console
==>pattern=>a
==>on_recv=>a?sub
>]
fhatos> *b
==>a?sub
fhatos> a->12
==>12
fhatos> *a
==>12
fhatos> *b
==>a?sub
   [■]                          [■]
  ╱   ╲                        ╱   ╲
[■]    [■]        [sub]     [■]     [■]
      ╱   ╲      ⋰     ⋱   ╱   ╲
   [■]     [12]@a       [12]@b  [■]

subscribes to a with bcode of the form \$f(a) → b\$. 12 is written to a which triggers the subscription bcode to write 12 to b.

  • memory

  • thread

fhatos> a -> 'axel'
==>'axel'
fhatos> *a
==>'axel'
fhatos> *a + ' fantaxel'
==>'axel fantaxel'
fhatos> *a
==>'axel'
fhatos> @a + ' fantaxel'
==>'axel fantaxel'@a
fhatos> *a
==>'axel fantaxel'@a
fhatos> thread[[
          loop=>from(a,0).plus(1).to(a).is(gt(10)).true.to(/abc/halt),
          halt=>false,
          delay=>nat[0]]]@abc
>thread[
==>loop=>from(a,0).plus(1).to(a).is(gt(10)).map(true).to(/abc/halt)
==>halt=>false
==>delay=>nat[0]
>]@abc
Example 1. Controlling Base Value Bit Encoding
The bit-length of int and real can be specified at boot time via the boot-loader. Other machines in the cluster with a different bit-length encodings can still be communicated with. However, overflow is possible, but can be automatically checked using types in /mmadt/ext/ such as: int8, int16, int32.
fhatos> int[6].inspect()
>[
==>type=>[
===>id=>/mmadt/int
===>obj=>[int][_]
===>dom=>[id=>/mmadt/obj,coeff=>[1,1]]
===>rng=>[id=>/mmadt/int,coeff=>[1,1]]
=>]
==>value=>[
===>obj=>6
===>encoding=>int32_t
=>]
>]
fhatos> real[6.0].inspect()
>[
==>type=>[
===>id=>/mmadt/real
===>obj=>[real][_]
===>dom=>[id=>/mmadt/obj,coeff=>[1,1]]
===>rng=>[id=>/mmadt/real,coeff=>[1,1]]
=>]
==>value=>[
===>obj=>6.00000
===>encoding=>float_t
=>]
>]
fhatos> /sys/router/config/resolve/auto_prefix ->
          *(_) + |[/mmadt/ext/]             (1)
>[
==>noobj
==>noobj
==>noobj
==>noobj
==>noobj
==>noobj
==>noobj
==>noobj
==>noobj
==>noobj
==>noobj
>]
fhatos> a -> int8[126]                       (2)
==>int8[126]
fhatos> @a + 1
==>int8[127]@a
fhatos> @a + 1
==>int8[128]@a
fhatos> @a + 1                               (3)
[ERROR] [/sys/scheduler] [/sys/lang/parser] 129@a is not a /mmadt/ext/int8 as defined by uint8()[is(gte(-127)).is(lte(128))]
	  thrown at inst int8[128]@a => plus(0?int=>1)[cpp] [0=>'@a + 1                               // <3>',0?int=>1]
1 Including /mmadt/ext objs in the router’s automatic URI resolution.
2 Constructing an int constrained to values from -127 to 128.
3 Triggering int8 type error by overflowing its numeric range.

Functional Types

The wildcard feature of the fURI scheme makes it possible to access instructions associated with a particular type.

fhatos> */mmadt/int/#
==>[int][[_]]
==>as?obj<=int(isa?noobj<=obj(/mmadt/uri)[noobj])[cpp]
==>div?int<=int(0?int=>isa?noobj<=obj(/mmadt/int)[noobj])[cpp]
==>gt?bool<=int(_)[cpp]
==>gte?bool<=int(_)[cpp]
==>inspect?rec<=int(_)[cpp]
==>lt?bool<=int(_)[cpp]
==>lte?bool<=int(_)[cpp]
==>minus?int<=int(0?int=>isa?noobj<=obj(/mmadt/int)[noobj])[cpp]
==>mod?int<=int(isa?noobj<=obj(/mmadt/int)[noobj])[cpp]
==>mult?int<=int(0?int=>isa?noobj<=obj(/mmadt/int)[noobj])[cpp]
==>neg?int<=int(isa?noobj<=obj(/mmadt/int)[noobj])[cpp]
==>plus?int<=int(0?int=>isa?noobj<=obj(/mmadt/int)[noobj])[cpp]
Sugar-Less mm-ADT

trill In the code example above, the expression to import /mmadt/ext is pretty intense looking, to say the least.

/sys/router/config/resolve/auto_prefix ->  *(_) + \|[/mmadt/ext/]

The line above looks daunting because it contains numerous syntactic sugars. Specifically, the binary and unary operators (binary), * (unary), (unary), + (binary), and \| (unary). Each of these symbols ultimately parse down to an inst. Each having that familiar functional form of f(a,b,c,…​). For example, the _sugar free representation of the expression above is:

start(</sys/router/config/resolve/auto_prefix>). (1)
 ref(                                            (2)
  from(_).                                       (3)
  plus(                                          (4)
    block(</mmadt/ext>)))                        (5)
1 Evaluate the mm-ADT bcode with uri[/sys/…​]. a …​
2 Use uri[/sys/…​] as the address to store a value in an underlying structure. a = …​
3 Fetch the value to store from the uri[/sys/…​]. a = get(a) …​
4 Add to the value stored at uri[/sys/…​] to …​ a = get(a) + …​.
5 …​ uri[/mmadt/ext]. a = get(a) + b.

Given that uri[/sys/router/config/resolve/auto_prefix] resolves to a lst of uris, uri[/mmadt/ext] is added that that lst and the updated lst is written back to uri[/sys/router/config/resolve/auto_prefix].

The one instruction that was not discussed above is block (sugar’d |). This is perhaps the most useful instruction in the whole of mm-ADT and knowing how to uses is absoluately crucial to being competent with the language. Moreover, when block is understood, so is a large portion of the language understood as well. Before diving into block, it’s important to first realize how instructions are evaluated. For this, the fundamental, immutable instruction apply (sugar’d .) is the perfect place to start.

Inst Evaluation Mechanics

An mm-ADT inst is an instruction. More generally, a function. More abstractly, a function. Syntactically, an inst has the form:

\[\tt{obj} := \tt{type}(\tt{frame})[\tt{value}]@\tt{ref}.\]

Starting with the template above, components will be removed to highlight various inst forms and functions.

  1. \(\tt{type}(\tt{frame})[\tt{value}]@\tt{ref}\): The complete form is a referenced inst and is used with coroutines.

  2. \(\tt{type}(\tt{frame})[\tt{value}]\): Without a reference location, the obj is a standard inst.

  3. \(\tt{type}(\tt{frame})[]\): Without a reference or value, the obj is a proto inst resolved to a standard inst during compilation or runtime.

  4. \(\tt{type}()[]\): Without a reference, value, or frame, the obj is a zero-arg proto inst and is resolved during compilation or runtime.

  5. \(\tt{type}\): Without a reference, value, frame, or respective tokens, the obj is an inst reference which can be dereferenced to yield the corresponding inst implementation.

type?rng{coeff}<=dom{coeff}(arg1, arg2, ...) [bcode]
\[f(\mathcal{Dom}^{C} \times A_1 \times A_2 \times \ldots) \rightarrow \mathcal{Rng}^{C}\]

The fURI query type-specification is more advanced and requires an understanding of structure query processors. As such, for now, realize an inst to have the form:

type(arg1, arg2, ...) [bcode]
\[f(\mathcal{Obj}_{\tt{dom}} \times A_1 \times A_2 \times \ldots) \rightarrow \mathcal{Obj}_{\tt{rng}}\]

In order to evaluate an inst an obj must be applied to it. Application is sugar’d ..

      inst(arg1, arg2, ...)
obj_d.inst(arg1, arg2, ...)
      inst(arg1, arg2, ...) => obj_r

When an obj is applied to an inst, the obj is called the left-hand side obj. This obj is the catalyst for a cascade of events that take place across the inst arguments and internal bcode. The sequence of events are diagrammatically represented in the graphical explanation below where each line is a new timestep in the process.

        ┌────────────────────┐
        ├──────────────┐     │
        ├────────┐     │     │
obj_d ──├─> inst(arg1, arg2, ...)
        │         └─────┤   ┌─┘
        │               │   │
        └─────────────>[x.y.z]─────> obj_r
      inst(arg1, arg2, ...)             [x.y.z]             (1)
obj_d.inst(arg1, arg2, ...)             [x.y.z]             (2)
      inst(obj_d.arg1, obj_d.arg2, ...) [x.y.z]             (3)
      inst(arg1_d, arg2_d, ...)         [obj_d.x.y.z]       (4)
      inst(arg1_d, arg2_d, ...)         [x.y.z => obj_r]    (5)
      inst(arg1, arg2, ...) => obj_r    [x.y.z]             (6)
1 The inst with a collection of arguments and a bcode body called inst_f.
2 A left-hand side obj is applied to the inst.
3 The left-hand side obj is split across all arguments and applied to each.
4 When all argument applications have completed, the left-hand side obj percolates through the bcode.
5 The right-hand side obj produced by the bcode is the result of the application.
6 The right-hand side obj becomes the input to the next inst in the large bcode expression (not shown).

The diagram states that the input obj is applied to each argument, the result of which are the actual arguments provided to the inst. The inst is thus, generally defined as:

\[ \begin{align*} x \cdot f(args...) & \rightarrow y \\ f(x,x \cdot args...) & \rightarrow y \\ f(x,x \cdot args_1, x \cdot args_2, ...) & \rightarrow y \\ \end{align*} \]

What separates inst from other poly types such as lst and rec (discussed next) is that it mounts a thread-local structure on the router called a fos:frame. The router supports a chain fos:frame structures and, in this way, fos:frame serves the purpose of a callstack, where the arguments of the inst can be dereferenced within the body of the inst.

fhatos> 34.make_bigger(a=>plus(10))[plus(*a)]
==>88

In the example above, make_bigger is defined "on the fly" (a "named lambda", if one chooses to see it as such) where the argument a can be dereferenced within the body of the inst [ …​ ]. The input to the body of the inst is, as can be expected, the left-hand side int[34].

Generalized Poly Evaluation Mechanics

sopapilla The fos:frame is the only aspect of an inst that makes it unique because every poly-type supports the same internally recursive application of an left-hand side obj. For example, see how the internal objs if a lst are effected by the application of an obj outside of the lst.

Lst Application
fhatos> 2.lst[[1,plus(2),mult(plus(3)),'a']]
>[
==>1
==>4
==>10
==>'a'
>]

Note that the application is recursive. For example, 2.mult(plus(3)) is evaluated as follows:

\[ \begin{align*} 2 \cdot \times(+(3)) & \rightarrow 10 \\ \times(2,2 \cdot +(3)) & \rightarrow 10 \\ \times(2, +(2,2 \cdot 3)) & \rightarrow 10 \\ \times(2, +(2,3)) & \rightarrow 10 \\ \times(2, 5) & \rightarrow 10 \\ 10 & \rightarrow 10 \\ \end{align*} \]
Rec Application

A rec behaves in a similar manner to lst and inst when a left-hand side obj is applied to it. However, what makes rec interesting and useful beyond a data storage structure is it’s delayed evaluation semantics denoted by .

fhatos> 2.rec[[is(gt(2)) => plus(2), _ => 0]]
[ERROR] [/sys/scheduler] [/mmadt/obj] /mmadt/gt?dom=/mmadt/obj&dc=1,1&rng=/mmadt/bool&rc=1,1 inst unresolved
	         lhs id      inst id      resolve obj
	  ->[/mmadt/obj] /mmadt/gt => gt()

This feature of rec make it both a data structure and a flow control structure as once an obj has been applied to rec, the values of rec can be "drained". For instance, if is implemented with a two entry rec, where one entry maps to noobj.

fhatos> /io/console/config/nest -> 0                  (1)
==>0
fhatos> {1,2,3}.[is(gt(2)) => _, _ => noobj]          (2)
[ERROR] [/sys/scheduler] [/mmadt/obj] /mmadt/gt?dom=/mmadt/obj&dc=1,1&rng=/mmadt/bool&rc=1,1 inst unresolved
	         lhs id      inst id      resolve obj
	  ->[/mmadt/obj] /mmadt/gt => gt()
fhatos> {1,2,3}.[is(gt(2)) => _, _ => noobj]>-        (3)
[ERROR] [/sys/scheduler] [/mmadt/obj] /mmadt/gt?dom=/mmadt/obj&dc=1,1&rng=/mmadt/bool&rc=1,1 inst unresolved
	         lhs id      inst id      resolve obj
	  ->[/mmadt/obj] /mmadt/gt => gt()
1 Reducing the console’s display depth for nested structures (purely aesthetic).
2 A stream of objs is applied one-by-one to the rec yielding a new internally-applied rec.
3 The internally-applied rec is "drained" via the merge inst (sugar’d >-).

In the above example, since 1 and 2 were mapped to noobj, they are effectively removed from the execution pipeline. However, because 3 is gt(2), it is mapped to _ (its self). Thus, when >- is applied to this rec, the result is {noobj,noobj,3} which is equivalent to {3}. In this way, rec is both a data structure and a flow control structure.

It’s not difficult to realize how an "if"-rec generalizes to support the various plays on one of computing’s most important concepts: the branch.

  • if-else

  • switch

  • guard

  • pattern

  • hash

fhatos> {1,2,3}-|[
          is(gt(2)) => mult(-1),      (1)
          _         => mult(100)]     (2)
==>1
==>2
==>3
fhatos> {1,2,3}-|[
          is(gt(2)) => mult(-1),
          _         => mult(100)]>-
==>100
==>200
==>-3
1 The if branch.
2 The else branch.
fhatos> {1,2,3}-<[
          is(gt(0)) => mult(-1),
          is(gt(1)) => mult(0),
          is(gt(2)) => _]
>[
==>is(gt(0))=>-1
>]
>[
==>is(gt(0))=>-2
==>is(gt(1))=>0
>]
>[
==>is(gt(0))=>-3
==>is(gt(1))=>0
==>is(gt(2))=>3
>]
fhatos> {1,2,3}-<[
          is(gt(0)) => mult(-1),
          is(gt(1)) => mult(0),
          is(gt(2)) => _]>-
==>-1
==>-2
==>0
==>-3
==>0
==>3
fhatos> {1,2,3}.[
==>1
==>2
==>3
fhatos> --- todo
conditional hash
fhatos> --- todo
The merge (sugar’d >-) instruction has a correlate: split (sugar’d -<). The way to think of these two instructions is that they either branch a serial execution pipeline (split) or the join a collection of parallel executing pipelines (merge). Interestingly, the application of an obj to a poly implements the split instruction. So why does an explicit split instruction exist? Because there are other ways in which branching pipelines can be defined and evaluated. This will be discussed later when discussing fos:thread, fos:coroutine, and fos:fiber.
Obj Application

The universal application of . (apply) implies that every obj is a function as every obj can have another obj applied to it. This is, in fact, the case.

fhatos> 1.plus(1)         (1)
==>2
fhatos> 1. 2              (2)
==>2
fhatos> 1.2.2             (3)
==>2
fhatos> [1,2,3].<1>       (4)
==>2
fhatos> [a=>1,b=>2].b     (5)
==>2
1 int[1] applied to inst[plus(1)].
2 int[1] applied to int[2] (the space before . is necessary to avoid parsing as a real).
3 real[1.2] applied to int[2].
4 lst\[[1,2,3]] applied to the uri[1].
5 rec\[[a⇒1,b⇒2]] applied to the uri[b].

X

noobj

bool

int

real

str

uri

lst

rec

inst

bcode

noobj

x

y

a

b

c

d

e

f

g

bool

x

y

z

a

b

c

d

e

f

g

int

x

y

z

a

b

c

d

e

f

g

real

x

y

z

a

b

c

d

e

f

g

str

x

y

z

a

b

c

d

e

f

g

uri

x

y

z

a

b

c

d

e

f

g

lst

x

y

z

a

b

c

d

e

f

g

rec

x

y

z

a

b

c

d

e

f

g

inst

x

y

z

a

b

c

d

e

f

g

bcode

x

y

z

a

b

c

d

e

f

g

Values

By Value vs. By Reference
age[45]@x => plus(10) => age[55]@x
    ^                        ^
   @|                        |
    x------------------------/
   *|
    v
age[45]  =>  plus(10) => age[55]
fhatos> age -> |(is(gt(0)).is(lt(120)))
[ERROR] [/sys/scheduler] [/mmadt/int] 0 accessed as uri
	  thrown at inst age => gt(0) [0=>'age -> |(is(gt(0)).is(lt(120)))',code=>'age -> |(is(gt(0)).is(lt(120)))']
fhatos> a -> age[45]
[ERROR] [/sys/scheduler] [/sys/lang/parser] age is an undefined type
fhatos> *a.inspect()
[ERROR] [/sys/scheduler] [/mmadt/obj] /mmadt/inspect?dom=/mmadt/obj&dc=1,1&rng=/mmadt/rec&rc=1,1 inst unresolved
	         lhs id      inst id           resolve obj
	  ->[/mmadt/obj] /mmadt/inspect => inspect(_)
fhatos> @a.inspect()
[ERROR] [/sys/scheduler] [/mmadt/obj] /mmadt/inspect?dom=/mmadt/obj&dc=1,1&rng=/mmadt/rec&rc=1,1 inst unresolved
	         lhs id      inst id           resolve obj
	  ->[/mmadt/obj] /mmadt/inspect => inspect(_)
fhatos> a?sub -> |print(_)
a?suba?sub==>a?sub
fhatos> a -> 12
==>12
fhatos> @a.inspect()
>[
==>type=>[
===>id=>/mmadt/int
===>obj=>[int][_]
===>dom=>[id=>/mmadt/obj,coeff=>[1,1]]
===>rng=>[id=>/mmadt/int,coeff=>[1,1]]
=>]
==>value=>[
===>id=>a
===>obj=>12
===>encoding=>int32_t
=>]
==>sub=>[
===>source=>/io/console
===>pattern=>a
===>on_recv=>a?sub
=>]
>]
fhatos> @a.plus(1)
==>13@a
fhatos> @a.plus(1).plus(1)
==>15@a

Types

Every mmADT obj is typed. A type is an mmADT obj. A obj can serve as a value in one situation and as a type in another. Types can be typed.

Bytecode and Instruction Types
User Defined Types

ginger mm-ADT is a structurally typed language, whereby if an obj A matches obj B, then A is a type of B. An obj type is a simply an mm-ADT program that verifies instances of the type. For instance, if a natural number \(\mathbb{N}\) is any non-negative number, then natural numbers are a subset (or refinement) of int.

fhatos> /type/int/nat -> |is(gt(0))
[ERROR] [/sys/scheduler] [/mmadt/int] 0 accessed as uri
	  thrown at inst /type/int/nat => gt(0) [0=>'/type/int/nat -> |is(gt(0))',code=>'/type/int/nat -> |is(gt(0))']
fhatos> nat[6]
==>nat[6]
fhatos> nat[-6]
[ERROR] [/sys/scheduler] [/sys/lang/parser] -6 is not a /mmadt/ext/nat as defined by nat()[is(gte(0))]
fhatos> nat[3].plus(2)
==>nat[5]
fhatos> nat[3].mult(-2)
[ERROR] [/sys/scheduler] [/sys/lang/parser] -6 is not a /mmadt/ext/nat as defined by nat()[is(gte(0))]
	  thrown at inst nat[3] => mult(0?int=>-2)[cpp] [0=>'nat[3].mult(-2)',0?int=>-2]
Process Types

A simple mm-ADT program is defined below. The program is a specialization of the poly-type rec called thread, where thread is abstractly defined as

The thread object is published to the fURI endpoint esp32@127.0.0.1/scheduler/threads/logger. The scheduler spawns the program on an individual thread accessible via the target fURI. Once spawned, the setup function prints the thread’s id and halts.

The Router Structure

cooties 2 Every fhatOS machine has a single router. The function of the router is to:

  1. Route read/write requests to respective structures.

  2. Coordinate with remote routers on remote read/write requests.

  3. Manage pattern conflicts between structures.

  4. Manage fURI query extensions (? modulators).

fhatos> /io/console/config/nest->3
==>3
fhatos> */sys/router/#/
>[
==>/sys/router=>[
===>frame=>[rec][_]
===>config=>[
====>resolve=>[namespace=>[:=>/mmadt/,fos:=>/fos/],auto_prefix=>[,/mmadt/,/mmadt/ext/,/fos/,/fos/sys/,/fos/io/,/fos/sensor/,/fos/ui/,/fos/util/,/sys/],query=>[write=>[lock=>to_do]],default_config=>[query=>[write=>[sub=>noobj]]]]
==>]
===>query=>[
====>write=>[lock=>lock?obj{?}<=obj{?}()[cpp],sub=>sub?obj{?}<=obj()[cpp]]
==>]
===>structure=>[
====>/sys/#
====>/mnt/#
====>/boot/#
====>/fos/#
====>/mmadt/#
====>/io/#
====>+/#
====>/disk/#
====>/shared/#
====>/bus/#
==>]
=>]@/sys/router
>]

The router manages access to physical memory. Physical memory is partitioned by structures. The address space of a structure is the (query-less) fURI. Structures have an associated pattern fURI defining the boundaries of their storage space. Structures can not have overlapping address spaces. Every structure implements the structure.hpp and ultimately, is an obj.

  • There are structures that encode objs in physical memory (e.g. heap).

  • There are structures that encode objs on disk (e.g. fs — filesystem).

  • There are structures that encode objs on a remote broker (e.g. mqtt).

  • There are structures that encode objs in the Bluetooth hierarchy (e.g. bt).

  • There are structures that encode objs on RFID chips (e.g. rfid).

  • There are structures that encode objs as scoped variables when evaluating code (e.g. frame).

  • There are structures that encode other structures (e.g. mnt).

The aggregate of all structures accessible through the router defines the complete memory footprint of a FHatOs​ instance.

fhatos> a -> 'snowbutt'            (1)
==>'snowbutt'
fhatos> *a                         (2)
==>'snowbutt'
fhatos> a?sub -> |to(b)            (3)
==>a?sub
fhatos> *a?sub                     (4)
>sub[
==>source=>/io/console
==>pattern=>a
==>on_recv=>a?sub
>]
fhatos> a -> 'meangirl'            (5)
==>'meangirl'
fhatos> *b                         (6)
==>a?sub
1 A request to write str['snowbutt'] to uri[a] is sent to the router.
2 A request to read the obj at uri[a] is sent to the router.
3 A subscription request to receive notifications about uri[a] is sent to the router.
4 A request to read the subscriptions of uri[a] is sent to the router.
5 A request to write str['meangirl'] to uri[a] is sent to the router.
6 A request to read uri[b] is sent to the router.

The above example makes salient the router’s role is structure usage. Not only are read/write requests managed by the router, but also subscriptions and the evaluation of their associated on_recv-code. However, ultimately, the router serves as a simple singleton proxy to the structures it manages. It’s in the structures where the heavily lifting of the memory operations takes place.

Structure Reading and Writing

submarine Every structure supports 2 primary operations:

\$\text{read} : U \rightarrow O\$

The router is given a fURI u from U. The router locates the structure responsible for the fURI subspace containing u. The structure resolves u to an obj. If no obj is found, noobj is returned.

fhatos> from(u)
==>'fhatos'
fhatos> *u
==>'fhatos'

\$\text{write}: (U \times O) \rightarrow \emptyset\$

fhatos> u -> o
==>o
fhatos> u.ref(o)
==>o
fhatos> o.to(u)
==>o

A read accepts a direct fURI (called an id) or a match fURI (called a pattern). Within the category of id and pattern, there are node fURIs and branch fURIs. An example itemization is provided below:

  • id: an unambiguous fURI that references a single addressable location in the structure.

    • node: the address of a specific obj.

    • branch: the root address of a collection of objs.

  • pattern: a fURI containing one or more wildcard path segments (+ or #).

    • node: a pattern referencing zero or more objs.

    • branch: a pattern referencing zero or more collections of objs.

fhatos> a/a -> 1; a/b -> 2; a/b/c -> 3; a/b/d -> 4;
fhatos> *a/b            (1)
==>2
fhatos> *a/b/           (2)
>[
==>a/b=>2
>]
fhatos> *a/+            (3)
==>1
==>2
fhatos> *a/+/           (4)
>[
==>a/a=>1
==>a/b=>2
>]
fhatos> *a/#            (5)
==>1
==>2
==>3
==>4
fhatos> *a/#/           (6)
>[
==>a/a=>1
==>a/b=>2
==>a/b/c=>3
==>a/b/d=>4
>]

The first line in the example appears to be 4 individual statements. In fact, it is a single fluent expression. The signature of the end inst (sugar’d ;) is end?obj{0}<=obj{*}. This barrier step computes all monads up to it before emitting a noobj monad. With end, it’s possible to write mm-ADT in the classic statement-oriented, imperative-style where semincolons (effectively) separate atomic operations.

1 Dereferencing an id-node fURI to access a single obj.
2 Dereferencing an id-branch fURI to access a collection of objs.
3 Dereferencing a pattern-node fURI to access objs at respective nodes.
4 Dereferencing a pattern-branch fURI to access objs at respective branches.
Query Processors

nelson Every fURI can have any number of key/value(s) pairs attached to it via the ? query encoding scheme defined by the W3C URI specification. Modules can be added to structures enabling different behaviors on read/write given associated, relevant ? parameters. Example modules that come preloaded with fhATos​ are:

  1. pubsub: supports asynchronous, event-based access to structure objs.

    1. a?sub → _ (subscribe )

    2. a?sub → noobj (unsubscribe)

    3. sub[source⇒uri, pattern⇒uri, on_recv⇒obj]

    4. msg[target⇒uri, payload⇒obj, retain⇒bool]

  2. lock: provides resource locking semantics to reading and writing objs in a concurrent environment.

    1. a?lock=w (prevent writes to the obj at a)

    2. a?lock=rw (prevent reads and writes to the obj at a)

    3. a?lock=false (unlock the obj at a)

  3. type: provides an obj type system encoded within an obj’s type fURI.

    1. nat?dom=int&dc=1,1&rng=int&rc=1,1 (the inst signature of nat?int⇐int()[…​])

Other modules can be created and deployed across a FHaToS​ cluster.

Query Free fURIs
The address space of a structures does not include the query parameters. Query parameters are used by structures to modulate the semantics of a read/write operation and are never used as the address of an obj. However, nothing prevents the obj at an address to be a uri[] with a query. Be sure to use the non-sugar’d < > fURI syntax when multiple values are associated with a key as the value separating , will be preferentially parsed as a lst, rec, or inst argument separator.
fhatos> abc?k1=v1&k2=v2&k3=v3                     (1)
==>abc?k1=v1&k2=v2&k3=v3
fhatos> abc?k1=v1,v2&k2=v3&k3=v4,v5,v6            (2)
==>abc?k1=v1,v2&k2=v3&k3=v4,v5,v6
fhatos> <abc?k1=v1,v2&k2=v3&k3=v4,v5,v6>          (3)
==>abc?k1=v1,v2&k2=v3&k3=v4,v5,v6
fhatos> <abc?k1=v1,v2&k2=v3&k3=v4,v5,v6> -> 12    (4)
==>12
fhatos> *abc
==>12
fhatos> abc -> <abc?k1=v1,v2&k2=v3&k3=v4,v5,v6>   (5)
==>abc?k1=v1,v2&k2=v3&k3=v4,v5,v6
fhatos> *abc
==>abc?k1=v1,v2&k2=v3&k3=v4,v5,v6
1 Sugar’d uri syntax can be used when no commas are present in the fURI.
2 Multiple values are deliminated using commas.
3 To ensure proper parsing, multi-value query uris[] should be wrapped in < > brackets.
4 The query of a fURI is stripped when used as a structure address.
5 The query of a fURI is not stripped when used as a value at a struture address.
1.plus(2)

Embedding

mm-ADT was designed to support the creation and manipulation of abstract data types — the "ADT" in mm-ADT. When expressing abstract data types is natural, then it’s possible to leverage multiple models such as key/value, document, relational, vector, graph, and the various nooks and crannies between — the "mm" in mm-ADT.

mm-ADT’s URI addressing scheme makes it possible to embed an array data types into the underlying FhATOs​ structure. This section will explore the following considerations when designing a multi-model abstract data type.

  1. spatial encodings

  2. schema encodings

  3. language encodings

Spatial Consideration when Embedding
fhatos> 1.plus(2)
==>3

A matrix is an \$n \times m\$ data structure composed of \$n\$ vectors/row, each with \$m\$ elements/columns. A relational database table is an example of a matrix, where the entries typically span numeric and non-numeric data types. Three general approaches to embedding a matrix or table into a fos:structure are presented below, where each makes different space/time tradeoffs.

 \    |   |    /
 [■] [■] [■] [■]

 -[■] [■] [■] [■]-

 [■] [■] [■] [■]
 /    |   |    \
​
[■]-[■]-[■]-[■]

[■]-[■]-[■]-[■]

[■]-[■]-[■]-[■]
​
​
[■]-[■]-[■]-[■]
|   |   |   |
[■]-[■]-[■]-[■]
|   |   |   |
[■]-[■]-[■]-[■]
​
fhatos> --- write matrix
fhatos> m/0/0 -> 0
==>0
fhatos> m/0/1 -> 1
==>1
fhatos> m/0/2 -> 2
==>2
fhatos> m/0/3 -> 3
==>3
fhatos> m/1/0 -> 4
==>4
fhatos> m/1/1 -> 5
==>5
fhatos> m/1/2 -> 6
==>6
fhatos> m/1/3 -> 7
==>7
fhatos> m/2/0 -> 8
==>8
fhatos> m/2/1 -> 9
==>9
fhatos> m/2/2 -> 10
==>10
fhatos> m/2/3 -> 11
==>11
fhatos> --- read matrix
==>x
fhatos> *m/0/0      (1)
==>0
fhatos> *m/0/+      (2)
==>0
==>1
==>2
==>3
fhatos> *m/+/0      (3)
==>0
==>4
==>8
fhatos> --- write matrix
fhatos> m/0 -> [0,1,2,3]
=>[
===>0
===>1
===>2
===>3
=>]
fhatos> m/1 -> [4,5,6,7]
=>[
===>4
===>5
===>6
===>7
=>]
fhatos> m/2 -> [8,9,10,11]
=>[
===>8
===>9
===>10
===>11
=>]
fhatos> --- read matrix
==>x
fhatos> *m/0/0      (1)
==>0
fhatos> *m/0/+      (2)
==>0
==>1
==>2
==>3
fhatos> *m/+/0      (3)
==>0
==>4
==>8
fhatos> --- write matrix
fhatos> m -> [[0,1,2,3],
              [4,5,6,7],
              [8,9,10,11]]
=>[
=>[
===>0
===>1
===>2
===>3
=>]
=>[
===>4
===>5
===>6
===>7
=>]
=>[
===>8
===>9
===>10
===>11
=>]
=>]
fhatos> --- read matrix
==>x
fhatos> *m/0/0      (1)
==>0
fhatos> *m/0/+      (2)
==>0
==>1
==>2
==>3
fhatos> *m/+/0      (3)
==>0
==>4
==>8
1 Retrieve the first element of matrix m.
2 Retrieve the first row of matrix m.
3 Retrieve the first column of matrix m.

The above example demonstrates the power of structural embeddings. The platonic matrix m was embedded in a structure using 3 different representations: entry-wise, row-wise, and row-column wise. Next, each embedding was read: an element read, a row read and a column read. The expression used to read from each of the three embeddings is the same and so is the result. This is possible because a structure resolves up the fURI path hierarchy until it finds a match. Once found, it then traverses within the match to resolve the remaining path segments.

Table 1. Amortized Costs in Terms of Time
embedding single-element row-access column-access

entry

\$O(1)\$

\$O(n)\$

\$O(m)\$

row

\$O(m)\$

\$O(1)\$

\$O(m)\$

row_column

\$O(1)\$

\$O(1)\$

\$O(1)\$

The different embeddings also have different space costs, where space is defined as the amount of data accessed (i.e. retrieved from the structure) in order to satisfy the resolution of the respective fURI.

Table 2. Amortized Costs in Terms of Space
embedding single-element row-access column-access

entry

\$O(1)\$

\$O(n)\$

\$O(m)\$

row

\$O(n)\$

\$O(n)\$

\$O(n+m)\$

row_column

\$O(n+m)\$

\$O(n+m)\$

\$O(n+m)\$

                                    [a=>[b,c]]
                                       [■]
          [■]                         /   \
                              [b=>c][■]   [■][d=>e]

   [a=>[b=>c,d=>e]]]            [a=>[b=>c,d=>e]]]
           ^                            ^
           |                            |
           x                            x/

The Scheduler Process

A FhatOS Console

FhatOS Console

The FhaToS​ Console is a composite of 3 other actors:

  1. The Terminal (/sys/io/terminal/) provides thread-safe access to hardware I/O.

  2. The Parser (/sys/lang/parser/) converts string input to bytecode output.

  3. The Processor (/sys/lang/processor/) executes bytecode.

fURI and MQTT

MQTT is a publish/subscribe message passing protocol that has found extensive usage in embedded systems. Hierarchically specified topics can be subscribed and published to. In MQTT, there is no direct communication between actors, though such behavior can be simulated if an actor’s mailbox is a unique topic. fhAToS​ leverages MQTT, but from the vantage point of URIs instead of topics with message routing being location-aware. There exist three MQTT routers:

  1. MonadRouter: An MQTT router scoped to an active monad (thread) processing a monoid (program).

  2. MonoidRouter: An MQTT router scoped to a monoid (program).

  3. HostRouter: An MQTT router scoped to the current host (machine).

  4. ClusterRouter: An MQTT router scoped to the current intranet (cluster).

  5. GlobalRouter : An MQTT router scoped to the Internet.

  6. MetaRouter: An MQTT router dynamically scoped to other routers based on fURI endpoints.

fURI Router Scope Patterns
The more / in the fURI prefix, the more distributed the fURI repeat. * abc monad scoped fURI. * ~/abc monoid scoped fURI ("home directory" of executing program). * /abc host scoped fURI (rooted at localhost). * //abc cluster scoped fURI (hosted on the intranet). * //fhatos.org/abc globally scoped fURI (hosted on the internet)
Monoid power method
\[M = aM\]
fhatos> {1,2,3}
==>1
==>2
==>3
fhatos> {1,2,3}.plus(10)
==>11
==>12
==>13
fhatos> {1,2,3}.plus(_)
==>2
==>4
==>6
fhatos> {1,2,3}.plus(plus(_))
==>3
==>6
==>9

FhatOS Modules

Kernel Modules

mmADT Module (mmadt)

Type Module (mmadt:type)
Parser Module (mmadt:parser)

Scheduler Module (scheduler)

Router Module (router)

Core Modules

Pin Modules

GPIO (gpio)

Hardware devices with digital general purpose input/output (GPIO) can be manipulated with /fos/io/gpio.

PWM (pwm)

Pins that support pulse-wave modulation can be manipulated with /fos/io/pwm.

i2c (i2c)

Two wire access

FileSystem Module (fs)

Terminal Module (terminal)

REPL Module (repl)

Logging Module (log)

Embedded Systems Modules

Sensors

Actuators

Reference

mm-ADT Core Instructions

as [_]

block |

is

plus

fhatos> true.plus(false)
==>true
fhatos> 1.plus(2)
==>3
fhatos> 'a'.plus('b')
==>'ab'

mult

mod

lift ^

drop v

split -<

each =

within _/ \_

merge >-

from *

to ->

get @

pass -->

match ~

fhatos> [a=>2].match([a=>3])
[ERROR] [/mmadt/rec] match inst unresolved
	         lhs id      inst id    resolve obj
	  ->[/mmadt/rec] match => noobj
	 -->[/mmadt/rec] match => noobj
	--->[          ] match => noobj
fhatos> [a=>2].match([a=>_])
[ERROR] [/mmadt/rec] match inst unresolved
	         lhs id      inst id    resolve obj
	  ->[/mmadt/rec] match => noobj
	 -->[/mmadt/rec] match => noobj
	--->[          ] match => noobj

eq

neq

gt

lt

gte

lte

FhatOS Types

Process Types

thread

fiber

coroutine

PubSub Types

sub

sub[[:source=>_, :pattern=>_, :on_recv=>bcode[_]]]

msg

msg[[:target=>uri[_], :payload=>_, :retain=>bool[_]]]