9
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Coconutことはじめ

Last updated at Posted at 2019-09-11

RubyでいうElixir的な立ち位置?Pythonにコンパイルされる関数型言語、Coconut

今朝、Quoraでたまたま見つけました、Coconutという言語

Quora - Pythonは悪い言語ではないと思うのですが、関数型プログラミングを試みようとすると苦痛が大きいです。関数型Pythonのようなものは存在しますか (Rubyに対するElixirのような) ?

Coconutは、Pythonにコンパイルされる関数型プログラミング言語であり、Pythonの既存のライブラリも呼べるようです。詳しい説明は公式を参照するのが良いと思うので下に貼ります。
(たしかに、ちょっと立ち位置は違うかもしれませんが、RubyでいうElixir的な存在かもしれませんね)

Coconut - Simple, elegant, Pythonic functional programming.
Coconut - github

Coconutセットアップ

Coconut を動かすには Python が必要です。
(ちなみにちょっと試したいだけならPCにインストールすることなく、こちらのオンラインエディターで試せます)

セットアップはPythonが入っている環境で、ターミナルを立ち上げ、下記のコマンドを実行するだけ。
(Coconutの公式を覗くと、特にPythonのバージョンは選ばないとのことです)

pip install coconut

Coconutのインタプリタを立ち上げて、Pythonを書いてみる

早速Coconut Tutorialを参照しながら Coconut を動かしてみます。

Coconutのインタプリタを起動させるにはターミナル上で下記のコマンドを叩きます。

coconut

すると、下記のようになり、coconutに触れます。

Coconut Interpreter:
(type 'exit()' or press Ctrl-D to end)
>>>

終了する場合は Ctrl + D もしくはインタプリタ上で exit() と打ってください。

CoconutではPythonも扱えます。例えば

>>> print("Hello World")
Hello World
>>> 1 + 1
2

Python のチュートアリルにあるような下記のような式も書くことができます。

>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
      x = 0
      print('Negative changed to zero')
    elif x == 0:
      print('Zero')
    elif x == 1:
      print('Single')
    else:
      print('More')

More

for文も可能です。

>>> words = ['cat', 'window', 'defenestrate']
>>> words
['cat', 'window', 'defenestrate']
>>> for w in words:
      print(w)

cat
window
defenestrate

Coconutのインタプリタを立ち上げて、Coconutを書いてみる

ここからはCoconutならではの書き方で書いてみようと思います。
(といっても、大半はcoconut tutorial参考にしているだけですが)

pipeline-style programming(パイプ演算子)

>>> "hello, world!" |> print
hello, world!

prettier lambdas(lambda式)

>>> x -> x ** 2
<function <lambda> at 0x1034f48c8>

partial application(部分適用)

>>> range(10) |> map$(pow$(?, 2)) |> list
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

pattern-matching(パターンマッチング)

>>> match [head] + tail in [0, 1, 2, 3]:
    print(head, tail)
0 [1, 2, 3]

Destructuring assignment(分割代入)

>>> {"list": [0] + rest} = {"list": [0, 1, 2, 3]}

>>> rest
[1, 2, 3]

>>> print({"list": [0] + rest})
{'list': [0, 1, 2, 3]}

infix notation(中置記法)

>>> def add(x,y):
      return x + y

>>> add
<function add at 0x104811bf8>

>>> 5 `add` 3 * 6
23

operator functions

>>> product = reduce$(*)
>>> product
functools.partial(<built-in function reduce>, <built-in function mul>)

>>> def add(a,b):
      return a+b

>>> add
<function add at 0x1102992f0>

>>> product2 = reduce$(add)

>>> product2
functools.partial(<built-in function reduce>, <function add at 0x1102992f0>)

function composition

>>> def plus1(x) = x + 1

>>> def square(x) = x * x

>>> (plus1..square)(3) == 10
True

lazy lists

>>> (| print("hello,"), print("world!") |) |> consume
hello,
world!
deque([], maxlen=0)

parallel programming

>>> range(100) |> parallel_map$(pow$(2)) |> list
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296, 8589934592, 17179869184, 34359738368, 68719476736, 137438953472, 274877906944, 549755813888, 1099511627776, 2199023255552, 4398046511104, 8796093022208, 17592186044416, 35184372088832, 70368744177664, 140737488355328, 281474976710656, 562949953421312, 1125899906842624, 2251799813685248, 4503599627370496, 9007199254740992, 18014398509481984, 36028797018963968, 72057594037927936, 144115188075855872, 288230376151711744, 576460752303423488, 1152921504606846976, 2305843009213693952, 4611686018427387904, 9223372036854775808, 18446744073709551616, 36893488147419103232, 73786976294838206464, 147573952589676412928, 295147905179352825856, 590295810358705651712, 1180591620717411303424, 2361183241434822606848, 4722366482869645213696, 9444732965739290427392, 18889465931478580854784, 37778931862957161709568, 75557863725914323419136, 151115727451828646838272, 302231454903657293676544, 604462909807314587353088, 1208925819614629174706176, 2417851639229258349412352, 4835703278458516698824704, 9671406556917033397649408, 19342813113834066795298816, 38685626227668133590597632, 77371252455336267181195264, 154742504910672534362390528, 309485009821345068724781056, 618970019642690137449562112, 1237940039285380274899124224, 2475880078570760549798248448, 4951760157141521099596496896, 9903520314283042199192993792, 19807040628566084398385987584, 39614081257132168796771975168, 79228162514264337593543950336, 158456325028528675187087900672, 316912650057057350374175801344, 633825300114114700748351602688]

tail call optimization(末尾再帰)

>>> def factorial(n, acc=1):
    case n:
        match 0:
            return acc
        match _ is int if n > 0:
            return factorial(n-1, acc*n)

>>> factorial
<function factorial at 0x10fdb49d8>

>>> factorial(10)
3628800

algebraic data types(代数的データ型)

>> data Empty()
data Leaf(n)
data Node(l, r)

>>> Empty
<class '__main__.Empty'>

>>> Leaf
<class '__main__.Leaf'>

>>> Node
<class '__main__.Node'>

>>> def size(Empty()) = 0
>>> addpattern def size(Leaf(n)) = 1
>>> addpattern def size(Node(l, r)) = size(l) + size(r)

>>> size
addpattern(<function size at 0x1108022f0>)(*[<function size at 0x1109687b8>, <function size at 0x110a72378>])

coconut.vimを使ってVimにシンタックスハイライトを設定する

coconuts.vim - githubというものが既にあるので、そちらを使用します。

私は現在プラグイン管理に deinを使用しているので、plugins.tomlに下記のように記述します

[[plugins]]
repo = 'manicmaniac/coconut.vim'

vim-plug を使っている場合は、下記の一行を~/.vimrcに追加する形になるでしょうか?
(動作未確認なので、間違えていたらすみません)

Plug 'manicmaniac/coconut.vim'

language-coconutを用いてAtomにcoconutのシンタックスハイライトを設定する

Atomのシンタックスハイライト設定には下記のプラグインを使うようです。

language-coconut - github

Atom を開いて Preference から Installで検索窓に language-coconut を入れれば、パッケージは見つかると思います。

Coconutファイルを作成してプログラムを実行する

シンタックスハイライトの設定が完了したら、Coconutのファイルを作成してみます。
Coconutのファイル拡張は .cocoだそうです。なんだか、可愛いですね。

hello_world.cocoというファイルを作成してみます。

vim hello_world.coco

Pythonとは異なり、ファイルのヘッダーは下記のようになるようですが、これらはCoconutコンパイラー側で自動的に追加されるようなので、実際は不要のようです。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
from __future__ import print_function, absolute_import, unicode_literals, division

簡単なサンプルをファイルに書いてCoconutを実行してみます。

"hello, world!" |> print

コンパイラーを使ってCoconutファイルをコンパイルする

coconutコマンドを使って、hello_world.cocoをコンパイルする

coconut hello_world.coco

実行結果は下記の通り

$coconut hello_world.coco
Compiling         hello_world.coco ...
Compiled to       hello_world.py .

カレントにhello_world.pyが生成されました。
このhello_world.pyを開いてみると、コンパイルされたCoconutコードが確認できます。
ここに貼ろうと思ったのですが結構大量だったのでやめました。ご自身で確認してみてください。

pythonコマンドで実行すると、下記のような結果になります。

$python hello_world.py
hello, world!

今日はここまで。続きはまた後日、少しずつ書き進めていきます。

※関数型言語は不慣れなため、もし間違った解釈している所があれば教えていただけますと幸いです。

9
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?