Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Mochi is a dynamically typed programming language for functional programming and actor-style programming.
Its interpreter is written in Python3. The interpreter translates a program written in Mochi to Python3's AST / bytecode.
Factorial
.. code:: python
def factorial(n, m):
if n == 1:
m
else:
factorial(n - 1, n * m)
factorial(10000, 1)
# => 28462596809170545189064132121198688...
# Or
def factorial:
n: factorial(n, 1)
0, acc: acc
n, acc: factorial(n - 1, acc * n)
factorial(10000)
# => 28462596809170545189064132121198688...
FizzBuzz
~~~~~~~~
.. code:: python
def fizzbuzz(n):
match [n % 3, n % 5]:
[0, 0]: "fizzbuzz"
[0, _]: "fizz"
[_, 0]: "buzz"
_: n
range(1, 31)
|> map(fizzbuzz)
|> pvector()
|> print()
Actor
~~~~~
.. code:: python
def show():
receive:
message:
print(message)
show()
actor = spawn(show)
send('foo', actor)
actor ! 'bar' # send('bar', actor)
sleep(1)
# -> foo
# -> bar
'foo' !> spawn(show)
sleep(1)
# -> foo
['foo', 'bar'] !&> spawn(show)
# The meaning of the above is the same as the meaning of the following.
# spawn(show) ! 'foo'
# spawn(show) ! 'bar'
sleep(1)
# -> foo
# -> bar
def show_loop():
receive:
[tag, value]:
print(tag, value)
show_loop()
actor2 = spawn(show_loop)
actor2 ! ["bar", 2000]
sleep(1)
# -> bar 2000
['foo', 1000] !> spawn(show_loop)
sleep(1)
# -> foo 1000
[['foo', 1000],['bar', 2000]] !&> spawn(show_loop)
sleep(1)
# -> foo 1000
# -> bar 2000
Distributed Computing
.. code:: python
# comsumer.mochi
from mochi.actor.mailbox import KombuMailbox, ZmqInbox, SQSMailbox
def consumer():
receive:
'exit':
print('exit!')
other:
print(other)
consumer()
kombu_mailbox = KombuMailbox('sqs://<access_key_id>@<secret_access_key>:80//',
'<queue_name>',
dict(region='<region>'))
spawn_with_mailbox(consumer, kombu_mailbox)
zmq_mailbox = ZmqInbox('tcp://*:9999')
spawn_with_mailbox(consumer, zmq_mailbox)
sqs_mailbox = SQSMailbox('<queue_name>')
spawn_with_mailbox(consumer, sqs_mailbox)
wait_all()
.. code:: python
# producer.mochi
from mochi.actor.mailbox import KombuMailbox, ZmqOutbox, SQSMailbox
kombu_mailbox = KombuMailbox('sqs://<access_key_id>@<secret_access_key>:80//',
'<queue_name>',
dict(region='<region>'))
kombu_mailbox ! [1, 2, 3]
kombu_mailbox ! 'exit'
zmq_mailbox = ZmqOutbox('tcp://localhost:9999')
zmq_mailbox ! [4, 5, 6]
zmq_mailbox ! 'exit'
sqs_mailbox = SQSMailbox('<queue_name>')
sqs_mailbox ! [7, 8, 9]
sqs_mailbox ! 'exit'
Flask
.. code:: python
from flask import Flask
app = Flask('demo')
@app.route('/')
def hello():
'Hello World!'
app.run()
RxPY
~~~~
.. code:: python
# usage: mochi -no-mp timer.mochi
# original:
# https://github.com/ReactiveX/RxPY/blob/master/examples/parallel/timer.py
import rx
import concurrent.futures
import time
seconds = [5, 1, 2, 4, 3]
def sleep(t):
time.sleep(t)
return t
def output(result):
print('%d seconds' % result)
with concurrent.futures.ProcessPoolExecutor(5) as executor:
rx.Observable.from_(seconds)
.flat_map((s) -> executor.submit(sleep, s))
.subscribe(output)
# 1 seconds
# 2 seconds
# 3 seconds
# 4 seconds
# 5 seconds
aif (Anaphoric macro)
.. code:: python
macro aif(test, true_expr, false_expr):
quasi_quote:
it = unquote(test)
if it:
unquote(true_expr)
else:
unquote(false_expr)
aif([], first(it), "empty")
# => "empty"
aif([10, 20], first(it), "empty")
# => 10
.. code:: sh
$ pip3 install mochi
$ pip3 install flask Flask-RESTful Pillow RxPY # to run the examples
Optional Installation
.. code:: sh
$ pip3 install flask Flask-RESTful Pillow RxPY # to run the examples
$ pip3 install pyzmq # to use ZmqInbox and ZmqOutbox
$ pip3 install kombu # to use KombuMailbox
$ pip3 install boto # to use SQS as transport of KombuMailbox
$ pip3 install boto3 # to use SQSMailbox
Th error of the following may occur when you run Mochi on PyPy.
..
ImportError: Importing zmq.backend.cffi failed with version mismatch, 0.8.2 != 0.9.2
In this case, please change the version of cffi to 0.8.2 using pip on PyPy.
.. code:: sh
$ pip3 uninstall cffi
$ pip3 install cffi==0.8.2
Usage
-----
REPL
~~~~
.. code:: sh
$ mochi
>>>
loading and running a file
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: sh
$ cat kinako.mochi
print('kinako')
$ mochi kinako.mochi
kinako
$ mochi -no-mp kinako.mochi # not apply eventlet's monkey patching
kinako
byte compilation
~~~~~~~~~~~~~~~~
.. code:: sh
$ mochi -c kinako.mochi > kinako.mochic
running a byte-compiled file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: sh
$ mochi -e kinako.mochic
kinako
$ mochi -e -no-mp kinako.mochic # not apply eventlet's monkey patching
kinako
generating .pyc
~~~~~~~~~~~~~~~
.. code:: sh
$ ls
kagami.mochi
$ cat kagami.mochi
print('kagami')
$ mochi
>>> import kagami
kagami
>>> exit()
$ ls
kagami.mochi kagami.pyc
$ python3 kagami.pyc
kagami
Or
.. code:: sh
$ mochi -pyc kagami.mochi > kagami.pyc
$ python3 kagami.pyc
kagami
$ mochi -pyc -no-mp kagami.mochi > kagami.pyc # not apply eventlet's monkey patching
$ python3 kagami.pyc
kagami
Examples for each feature
-------------------------
Persistent data structures
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: python
[1, 2, 3]
# => pvector([1, 2, 3])
v(1, 2, 3)
# => pvector([1, 2, 3])
vec = [1, 2, 3]
vec2 = vec.set(0, 8)
# => pvector([8, 2, 3]
vec
# => pvector([1, 2, 3])
[x, y, z] = vec
x # => 1
y # => 2
z # => 3
get(vec, 0) # => 1
get(vec, 0, 2) # => [1, 2]
vec[0] # => 1
vec[0:2] # => [1, 2]
{'x': 100, 'y': 200}
# => pmap({'y': 200, 'x': 100})
ma = {'x': 100, 'y': 200}
ma.get('x') # => 100
ma.x # => 100
ma['x'] # => 100
ma2 = ma.set('x', 10000)
# => pmap({'y': 200, 'x': 10000})
ma # => pmap({'y': 200, 'x': 100})
get(ma, 'y') # => 200
ma['y'] # => 200
m(x=100, y=200)
# => pmap({'y': 200, 'x': 100})
s(1, 2, 3)
# => pset([1, 2, 3])
b(1, 2, 3)
# => pbag([1, 2, 3])
Function definitions
~~~~~~~~~~~~~~~~~~~~
.. code:: python
def hoge(x):
'hoge' + str(x)
hoge(3)
# => hoge3
Pattern matching
~~~~~~~~~~~~~~~~
.. code:: python
lis = [1, 2, 3]
# Sequence pattern
match lis:
[1, 2, x]: x
_: None
# => 3
match lis:
[1, &rest]: rest
_: None
# => pvector([2, 3])
foo_map = {'foo' : 'bar'}
# Mapping pattern
match foo_map:
{'foo' : value}: value
_: None
# => 'bar'
# Type pattern
# <name of variable refers to type> <pattern>: <action>
match 10:
int x: 'int'
float x: 'float'
str x: 'str'
bool x: 'bool'
_: 'other'
# => 'int'
match [1, 2, 3]:
[1, str x, 3]: 'str'
[1, int x, 3]: 'int'
_: 'other'
# => 'int'
num = union(int, float)
vector nums[num]
vector strs[str]
match nums([1, 2, 3]):
nums[x, y, z]: z
strs[x, y, z]: x
# => 3
Positive = predicate(-> $1 > 0)
Even = predicate(-> $1 % 2 == 0)
EvenAndPositive = predicate(-> ($1 % 2 == 0) and ($1 >= 0))
match 10:
EvenAndPositive n: str(n) + ':Even and Positive'
Even n: str(n) + ':Even'
Positive n: str(n) + ':Positive'
# => 10:Even and Positive
# Or pattern
match ['foo', 100]:
['foo' or 'bar', value]: value
_: 10000
# => 100
match ['foo', 100]:
[str x or int x, value]: value
_: 10000
# => 100
# Record pattern
record Person(name, age)
foo = Person('foo', 32)
match foo:
Person('bar', age):
'bar:' + str(age)
Person('foo', age):
'foo:' + str(age)
_: None
# => 'foo:32'
Records
~~~~~~~
.. code:: python
record Mochi
record AnkoMochi(anko) < Mochi
record KinakoMochi(kinako) < Mochi
anko_mochi = AnkoMochi(anko=3)
isinstance(anko_mochi, Mochi)
# => True
isinstance(anko_mochi, AnkoMochi)
# => True
isinstance(anko_mochi, KinakoMochi)
# => False
match anko_mochi:
KinakoMochi(kinako): 'kinako ' * kinako + ' mochi'
AnkoMochi(anko): 'anko ' * anko + 'mochi'
Mochi(_): 'mochi'
# => 'anko anko anko mochi'
record Person(name, age):
def show(self):
print(self.name + ': ' + self.age)
foo = Person('foo', '32')
foo.show()
# -> foo: 32
# runtime type checking
record Point(x:int, y:int, z:optional(int))
Point(1, 2, None)
# => Point(x=1, y=2, z=None)
Point(1, 2, 3)
# => Point(x=1, y=2, z=3)
Point(1, None, 3)
# => TypeError
Bindings
~~~~~~~~
.. code:: python
x = 3000
# => 3000
[a, b] = [1, 2]
a
# => 1
b
# => 2
[c, &d] = [1, 2, 3]
c
# => 1
d
# => pvector([2, 3])
Data types, like algebraic data types
.. code:: python
data Point:
Point2D(x, y)
Point3D(x, y, z)
# The meaning of the above is the same as the meaning of the following.
# record Point
# record Point2D(x, y) < Point
# record Point3D(x, y, z) < Point
p1 = Point2D(x=1, y=2)
# => Point2D(x=1, y=2)
p2 = Point2D(3, 4)
# => Point2D(x=3, y=4)
p1.x
# => 1
Pattern-matching function definitions
.. code:: python
data Point:
Point2D(x, y)
Point3D(x, y, z)
def offset:
Point2D(x1, y1), Point2D(x2, y2):
Point2D(x1 + x2, y1 + y2)
Point3D(x1, y1, z1), Point3D(x2, y2, z2):
Point3D(x1 + x2, y1 + y2, z1 + z2)
_: None
offset(Point2D(1, 2), Point2D(3, 4))
# => Point2D(x=4, y=6)
offset(Point3D(1, 2, 3), Point3D(4, 5, 6))
# => Point3D(x=5, y=7, z=9)
def show:
int x, message: print('int', x, message)
float x, message: print('float', x, message)
_: None
show(1.0, 'msg')
# -> float 1.0 msg
# => None
FileMode = options('r', 'w', 'a', 'r+', 'w+', 'a+')
def open_file:
str path, FileMode mode:
open(path, mode)
str path:
open(path, 'r')
Anonymous function
~~~~~~~~~~~~~~~~~~
.. code:: python
# Arrow expression.
add = (x, y) -> x + y
add(1, 2)
# => 3
add = -> $1 + $2
add(1, 2)
# => 3
foo = (x, y) ->
if x == 0:
y
else:
x
foo(1, 2)
# => 1
foo(0, 2)
# => 2
pvector(map(-> $1 * 2, [1, 2, 3]))
# => pvector([2, 4, 6])
Pipeline operator
~~~~~~~~~~~~~~~~~
.. code:: python
add = -> $1 + $2
2 |> add(10) |> add(12)
# => 24
None |>? add(10) |>? add(12)
# => None
Lazy sequences
~~~~~~~~~~~~~~
.. code:: python
def fizzbuzz(n):
match [n % 3, n % 5]:
[0, 0]: "fizzbuzz"
[0, _]: "fizz"
[_, 0]: "buzz"
_: n
result = range(1, 31) |> map(fizzbuzz)
pvector(result)
# => pvector([1, 2, fizz, 4, 'buzz', 'fizz', 7, 8, 'fizz', 'buzz', 11, 'fizz', 13, 14, 'fizzbuzz', 16, 17, 'fizz', 19, 'buzz', 'fizz', 22, 23, 'fizz', 'buzz', 26, 'fizz', 28, 29, 'fizzbuzz'])
pvector(result)
# => pvector([])
pvector(result)
# => pvector([])
# Iterator -> lazyseq
lazy_result = range(1, 31) |> map(fizzbuzz) |> lazyseq()
pvector(lazy_result)
# => pvector([1, 2, fizz, 4, 'buzz', 'fizz', 7, 8, 'fizz', 'buzz', 11, 'fizz', 13, 14, 'fizzbuzz', 16, 17, 'fizz', 19, 'buzz', 'fizz', 22, 23, 'fizz', 'buzz', 26, 'fizz', 28, 29, 'fizzbuzz'])
pvector(lazy_result)
# => pvector([1, 2, fizz, 4, 'buzz', 'fizz', 7, 8, 'fizz', 'buzz', 11, 'fizz', 13, 14, 'fizzbuzz', 16, 17, 'fizz', 19, 'buzz', 'fizz', 22, 23, 'fizz', 'buzz', 26, 'fizz', 28, 29, 'fizzbuzz'])
pvector(lazy_result)
# => pvector([1, 2, fizz, 4, 'buzz', 'fizz', 7, 8, 'fizz', 'buzz', 11, 'fizz', 13, 14, 'fizzbuzz', 16, 17, 'fizz', 19, 'buzz', 'fizz', 22, 23, 'fizz', 'buzz', 26, 'fizz', 28, 29, 'fizzbuzz'])
Trailing closures
~~~~~~~~~~~~~~~~~
.. code:: python
# The following trailing closure expression is passed to a function as the function’s first argument.
result = map([1, 2, 3]) ->
print($1)
$1 * 2
print(doall(result))
# -> 1
# -> 2
# -> 3
# => pvector([2, 4, 6])
def foreach(closure, seq):
doall(filter(closure, seq))
# The following trailing closure expression is passed to a function as the function’s first argument.
foreach([1, 2, 3]) (item) ->
new_item = item * 100
print(new_item)
# -> 100
# -> 200
# -> 300
# => pvector([])
# Or
def foreach(seq, closure):
doall(filter(closure, seq))
# The following trailing closure expression is passed to a function as the function’s final argument.
foreach([1, 2, 3]) @ (item) ->
new_item = item * 100
print(new_item)
# -> 100
# -> 200
# -> 300
# => pvector([])
Short form for keyword arguments and dict keys
.. code:: python
def foo(a, b, c):
a + b + c
a = 1
b = 2
c = 3
# This is the same as foo(a=a, b=b, c=c)
foo(=a, =b, =c))
# => 6
# This is the same as {'a': a, 'b': b}
{=a, =b}
# => pmap({'a': 1, 'b': 2})
Macros
.. code:: python
macro rest_if_first_is_true(first, &args):
match first:
quote(True): quasi_quote(v(unquote_splicing(args)))
_: quote(False)
rest_if_first_is_true(True, 1, 2, 3)
# => pvector([1, 2, 3])
rest_if_first_is_true("foo", 1, 2, 3)
# => False
macro pipeline(&args):
[Symbol('|>')] + args
pipeline([1, 2, 3],
map(-> $1 * 2),
filter(-> $1 != 2),
pvector())
# => pvector([4, 6])
Including a file at compile time
.. code:: sh
$ cat anko.mochi
x = 10000
y = 20000
.. code:: python
require 'anko.mochi'
x
# => 10000
x = 30000
require 'anko.mochi' # include once at compile time
x
# => 30000
Module
.. code:: python
module Math:
export add, sub
def add(x, y):
x + y
def sub(x, y):
x - y
Math.add(1, 2)
# => 3
.. code:: sh
$ cat foobar.mochi
foo = 'foo'
bar = 'bar'
.. code:: python
require 'foobar.mochi'
[foo, bar]
# => pvector(['foo', 'bar'])
foo = 'foofoofoo'
module X:
export foobar
require 'foobar.mochi'
def foobar:
[foo, bar]
X.foobar()
# => pvector(['foo', 'bar'])
[foo, bar]
# => pvector(['foofoofoo', 'bar'])
TODO
----
- Improve documentation
- Improve parsing
- Support type annotation
License
-------
MIT License
Contributors
------------
https://github.com/i2y/mochi/graphs/contributors
FAQs
Dynamically typed functional programming language
We found that mochi demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.