Cuneiform語言

Cuneiform是用於大規模科學數據分析的開源工作流程語言。
它是促進並行計算的靜態類型的純函數式編程語言。它的特徵是有個全功能的foreign function interface，允許用戶集成來自很多外部編程語言的軟件。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庫）是通過foreign function interface來集成的。通過它可以組合，比如允許通過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的記錄，第一個是字符串，而第二個是布爾值：
let r :  =
;
記錄可以要麼通過投影（projection）要麼通過模式匹配來訪問。下面的例子從記錄r，提取兩個字段a1和a2：
let a1 : Str = ( r|a1 );
let  = r;

**列表和列表處理**
進一步的，Cuneiform提供列表作為複合數據類型。下面的例子展示定義一個變量xs，作為一個有三個元素的一個文件：
let xs : File =
'a.txt', 'b.txt', 'c.txt' : File;
列表可以通過for和fold算子來處理。這裡的for算子可以接受多個列表，來逐個元素的處置列表（類似於Racket中的for/list、Common Lisp中的mapcar或Erlang中的zipwith）。
下面的例子展示如何在一個單一的列表上map，結果是一個文件列表：
for x 
下面的例子展示如何zip兩個列表，結果也是一個文件列表：
for x 
最後，列表可以使用fold算子來做聚集。下面的例子合計一個列表的元素：
fold acc = 0, x 

**並行執行**
Cuneiform是純函數式語言，就是說它不支持可變引用。作為結論，它可以使用獨立子項，將一個程序分解成可並行的各部份。Cuneiform調度器分佈這些部份到做工節點。此外，Cuneiform採用傳名調用求值策略，值只在它對計算結果有貢獻時計算。最後，外界函數應用是記憶化的，用來加速包含以前推導結果的計算。
例如，下列Cuneiform程序允許f和g的應用平行的運行，而h是有依賴的，它只在f和g二者完成的時候可以開始：
下列Cuneiform程序通過將函數f映射到一個三元素列表，創建了三個並行應用：
類似的，在記錄r的構造中，f和g的應用是獨立的，因此可以並行運行：

**例子**
下面是一個hello-world腳本：
def greet( person : Str ) -> 
in Bash *{
out="Hello $person"
}
( greet( person = "world" )|out );
這個腳本定義了採用Bash的一個任務greet，它對其字符串實際參數person預加上"Hello "。這個函數產生具有一個單一字符串字段的記錄out。應用greet，綁定實際參數person到字符串"world"，產生記錄。將這個記錄投影為它的字段out，求值出字符串"Hello world"。
可以通過定義採用Bash的一個任務來集成命令行工具：
def samtoolsSort( bam : File ) -> 
in Bash *{
sorted=sorted.bam
samtools sort -m 2G $bam -o $sorted
}
在這個例子中定義了任務samtoolsSort。它調用了工具SAMtools，處置一個BAM格式的輸入文件，併產生一個排序了也是BAM格式的輸出文件。

**發行歷史**
在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**
基於Swing的編輯器和Cuneiform 2.0.3的REPL
Cuneiform外表語法的第二個草案，首次發表於2015年3月，在Cuneiform的實現語言從Java到Erlang的遷移期間，持續使用了三年。求值不同於早期方式，解釋器歸約一個表達式，而非遍歷一個靜態圖。在外表語法保持使用這段時期，解釋器被形式化和簡化，這導致了第一個Cuneiform的語義規定。語法特徵是有了條件。但是，布爾值被編碼為列表，再度利用空列表為布爾值false，非空列表為布爾值true。遞歸後來作為形式化的副產品而增加。但是，靜態類型檢查，只在後來的版本3中介入。
下列腳本解壓一個壓縮文件，並把它分解為大小均勻的劃分：
deftask unzip(  : zip( File ) ) in bash *{
unzip -d dir $zip
out=`ls dir | awk '{print "dir/" $0}'`
}
deftask split(  : 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 ) -> 
in Bash *{
tar xf $tar
fileLst=`tar tf $tar`
}
let hg38Tar : File =
'hg38/hg38.tar';
let  =
untar( tar = hg38Tar );
faLst;

**引用==