Cuneiform語言

Cuneiform是用於大規模科學數據分析的開源科學工作流程系統。

它是促進並行計算的靜態類型的純函數式程式語言。它的特徵是有個全功能的外界函數接口，允許用戶集成來自很多外部程式語言的軟體。Cuneiform在組織層面上提供了一些設施，如條件分支和通用遞歸，使其具有圖靈完備性。

## 概述

Cuneiform嘗試拉近在科學工作流程系統如Apache Taverna、KNIME或Galaxy，與大規模數據分析程序模型如MapReduce或Pig Latin之間的間隙，同時提供函數式程式語言的通用性。

Cuneiform是用分布式Erlang實現的。如果運行在分布式模態下，它導出一個遵循POSIX的分布式文件系統，如Gluster或Ceph（用FUSE集成某個其他文件系統例如HDFS）。作為替代選擇，Cuneiform腳本可以執行在HTCondor或Apache Hadoop頂上。

Cuneiform受到了Peter Kelly的工作的影響，他提議函數式編程作為科學工作流程執行的模型。

故而，Cuneiform不同於其他的基於數據流程編程的工作流程語言，如並行腳本語言Swift (並行腳本語言)。

## 擴展軟體集成

外部工具和庫（比如R或Python庫）是通過外界函數接口來集成的。通過它可以組合，比如允許通過snippet節點使用外部軟體的KNIME，或為集成Java軟體提供BeanShell服務的Apache Taverna。通過定義採用外界語言的任務，就可能使用一個外部工具或庫的API。這種方式下，工具可以直接集成而不需要寫包裝器或重新實現工具。

目前支持的外界程式語言有：

- Bash
- Elixir
- Erlang
- Java
- JavaScript
- MATLAB
- GNU Octave
- Perl
- Python
- R
- Racket

計劃增加外界語言AWK和gnuplot。

## 類型系統

Cuneiform提供簡單的、靜態檢查的類型系統。雖然Cuneiform提供列表作為複合數據類型，它省略了傳統的列表訪問子（`head`和`tail`），以避免在訪問空列表時，可能引起的運行時間錯誤的可能性。轉而列表只能通過在其上`map`或`fold`，以全有或全無方式訪問。此外，Cuneiform（在組織層面）省略了算術，這排除了除以零的可能性。省略任何的部份定義運算，允許保證運行時間錯誤只能在外界代碼中引發。

### 基礎數據類型

Cuneiform提供的基礎數據類型有布爾值、字串和文件。這裡的文件被用來以任意格式在外界函數之間交換數據。

### 記錄和模式匹配

Cuneiform提供記錄（結構）作為複合數據類型。下面的例子展示定義一個變量`r`，作為有兩個欄位`a1`和`a2`的記錄，第一個是字串，而第二個是布爾值：

```swift
let r : <a1 : Str, a2 : Bool> =
  <a1 = "my string", a2 = true>;
```

記錄可以要麼通過投影（projection）要麼通過模式匹配來訪問。下面的例子從記錄`r`，提取兩個欄位`a1`和`a2`：

```swift
let a1 : Str = ( r|a1 );

let <a2 = a2 : Bool> = r;
```

### 列表和列表處理

進一步的，Cuneiform提供列表作為複合數據類型。下面的例子展示定義一個變量`xs`，作為一個有三個元素的一個文件：

```erlang
let xs : [File] =
  ['a.txt', 'b.txt', 'c.txt' : File];
```

列表可以通過`for`和`fold`算子來處理。這裡的`for`算子可以接受多個列表，來逐個元素的處置列表（類似於Racket中的`for/list`、Common Lisp中的`mapcar`或Erlang中的`zipwith`）。

下面的例子展示如何在一個單一的列表上`map`，結果是一個文件列表：

```ruby
for x <- xs do
  process-one( arg1 = x )
  : File
end;
```

下面的例子展示如何`zip`兩個列表，結果也是一個文件列表：

```ruby
for x <- xs, y <- ys do
  process-two( arg1 = x, arg2 = y )
  : File
end;
```

最後，列表可以使用`fold`算子來做聚集。下面的例子合計一個列表的元素：

```text
  fold acc = 0, x <- xs do
    add( a = acc, b = x )
  end;
```

## 並行執行

Cuneiform是純函數式語言，就是說它不支持可變引用。作為結論，它可以使用獨立子項，將一個程序分解成可並行的各部份。Cuneiform調度器分布這些部份到做工節點。此外，Cuneiform採用傳名調用求值策略，值只在它對計算結果有貢獻時計算。最後，外界函數應用是記憶化的，用來加速包含以前推導結果的計算。

例如，下列Cuneiform程序允許`f`和`g`的應用平行的運行，而`h`是有依賴的，它只在`f`和`g`二者完成的時候可以開始：

下列Cuneiform程序通過將函數`f`映射到一個三元素列表，創建了三個並行應用：

類似的，在記錄`r`的構造中，`f`和`g`的應用是獨立的，因此可以並行運行：

## 例子

下面是一個hello-world腳本：

```ruby
def greet( person : Str ) -> <out : Str>
in Bash *{
  out="Hello $person"
}*

( greet( person = "world" )|out );
```

這個腳本定義了採用Bash的一個任務`greet`，它對其字串實際參數`person`預加上`"Hello "`。這個函數產生具有一個單一字串欄位的記錄`out`。應用`greet`，綁定實際參數`person`到字串`"world"`，產生記錄`<out = "Hello world">`。將這個記錄投影為它的欄位`out`，求值出字串`"Hello world"`。

可以通過定義採用Bash的一個任務來集成命令行工具：

```ruby
def samtoolsSort( bam : File ) -> <sorted : File>
in Bash *{
  sorted=sorted.bam
  samtools sort -m 2G $bam -o $sorted
}*
```

在這個例子中定義了任務`samtoolsSort`。它調用了工具SAMtools，處置一個BAM格式的輸入文件，並產生一個排序了也是BAM格式的輸出文件。

## 發行歷史

版本｜出現日期｜實現語言｜發布平台｜外界語言

1.0.0｜2014年5月｜Java｜Apache Hadoop｜Bash, Common Lisp, GNU Octave, Perl, Python, R, Scala

2.0.x｜2015年3月｜Java｜HTCondor, Apache Hadoop｜Bash, BeanShell, Common Lisp, MATLAB, GNU Octave, Perl, Python, R, Scala

2.2.x｜2016年4月｜Erlang｜HTCondor, Apache Hadoop｜Bash, Perl, Python, R

3.0.x｜2018年2月｜Erlang｜分布式Erlang｜Bash, Erlang, Java, MATLAB, GNU Octave, Perl, Python, R, Racket

在2016年4月，Cuneiform的實現語言從Java切換成了Erlang，並且在2018年2月，它的主要發布執行平台從Apache Hadoop變更為分布式Erlang。此外，從2015年到2018年，HTCondor曾被作為可替代執行平台來維護。

Cuneiform的外表語法修訂過兩次，這反映在主版本號上。

### 版本1

在2014年5月發布的最初草案中，Cuneiform密切關聯於Make，它構造解釋器在執行期間要遍歷的靜態數據依賴圖。與後來版本的主要區別，是缺乏條件、遞歸或靜態類型檢查。區分文件和字串，是通過同波浪號`~`形成一個單一引用的字串。腳本的查詢表達式，通過`target`關鍵字來介入。Bash是預設外界語言。函數應用必須使用`apply`形式來完成，它接受`task`作為第一個關鍵字實際參數。一年之後，這種外表語法被一種精簡卻類似的版本所替代。

下面的例子腳本從一個FTP伺服器下載一個參考genome：

```
declare download-ref-genome;

deftask download-fa( fa : ~path ~id ) *{
    wget $path/$id.fa.gz
    gunzip $id.fa.gz
    mv $id.fa $fa
}*

ref-genome-path = ~'ftp://hgdownload.cse.ucsc.edu/goldenPath/hg19/chromosomes';
ref-genome-id = ~'chr22';

ref-genome = apply(
    task : download-fa
    path : ref-genome-path
    id : ref-genome-id
);

target ref-genome;
```

### 版本2

<image>

Cuneiform外表語法的第二個草案，首次發表於2015年3月，在Cuneiform的實現語言從Java到Erlang的遷移期間，持續使用了三年。求值不同於早期方式，解釋器歸約一個表達式，而非遍歷一個靜態圖。在外表語法保持使用這段時期，解釋器被形式化和簡化，這導致了第一個Cuneiform的語義規定。語法特徵是有了條件。但是，布爾值被編碼為列表，再度利用空列表為布爾值`false`，非空列表為布爾值`true`。遞歸後來作為形式化的副產品而增加。但是，靜態類型檢查，只在後來的版本3中介入。

下列腳本解壓一個壓縮文件，並把它分解為大小均勻的劃分：

```
deftask unzip( <out( File )> : zip( File ) ) in bash *{
  unzip -d dir $zip
  out=`ls dir | awk '{print "dir/" $0}'`
}*

deftask split( <out( File )> : file( File ) ) in bash *{
  split -l 1024 $file txt
  out=txt*
}*

sotu = "sotu/stateoftheunion1790-2014.txt.zip";
fileLst = split( file: unzip( zip: sotu ) );

fileLst;
```

### 版本3

Cuneiform外表語法的當前版本，比較於早期的草案，是嘗試拉近與主流函數式程式語言的間隙。它的特徵是簡單的靜態檢查的類型系統，並在列表之外介入記錄作為第二個複合數據結構類型。布爾值獨立為基礎數據類型。

下列腳本解包一個文件，結果為一個文件列表：

```
def untar( tar : File ) -> <fileLst : [File]>
in Bash *{
  tar xf $tar
  fileLst=`tar tf $tar`
}*

let hg38Tar : File =
  'hg38/hg38.tar';

let <fileLst = faLst : [File]> =
  untar( tar = hg38Tar );

faLst;
```

## 基本資料

- 日語原文：Cuneiform
- 編程范型：純函數式, 數據流程
- 設計者：Jörgen Brandt
- 狀態：Active
- 軟體版本週期：3.0.5
- 軟體版本週期：2022年5月27日
- 類型系統：簡單類型
- 受影響於：Swift (並行腳本語言)
- 作業系統：Linux, Mac OS
- 實作語言：Erlang
- 許可證：Apache許可證 2.0
- 文件擴展名：.cfl
- 發行時間：2013年