# n8n ↔︎ Haskell
## n8n → Haskell
n8n上で構築されたワークフローを、HaskellのコードやDSL(ドメイン固有言語)として出力・再構築可能にする。
### 例:n8n → Haskell DSLへの変換イメージ
1. フォームからリクエストを受け取る(Webhook)
2. データをバリデーション(Code Node)
3. APIを呼び出してDBに保存(HTTP Node)
4. Slack通知(Slack Node)
このn8nワークフローを、Haskell DSLでこう表現できるようにする:
```haskell
workflow "フォーム受付フロー" $ do
input <- receiveWebhook "contact-form"
validated <- validate input
_ <- callAPI "POST /orders" validated
notifySlack "受注処理が完了しました"
```
### 実装方法
1. n8nのワークフローファイル(JSON)をパース1. n8nのワークフローファイル(JSON)をパース
```haskell
{
"nodes": [
{ "id": "1", "type": "webhook", ... },
{ "id": "2", "type": "httpRequest", ... },
...
]
}
```
2. Haskell DSLとして再構成
- JSONをパースして、各ノードをHaskellの抽象構文木(AST)として変換
- それをコード生成(Pretty Print)
## Haskell → n8n
Haskell で業務ワークフローを抽象的な DSL(Free Monad)で記述し、それを n8n のワークフロー JSON に変換して自動化基盤として活用する。
**Free Monad** のユースケース:
- 業務処理の**抽象的な定義**と**具体的な解釈(インタプリタ)**を分離したい
- ワークフローを**記述(宣言)** → **解釈(実行 or 変換)**したい
- ロジックの**合成性・再利用性**を高めたい
### 構成イメージ
1. DSL定義(Functor)
```haskell
data WorkflowF next
= ReceiveWebhook String (String -> next)
| CallHttpAPI String String next
| NotifySlack String next
deriving Functor
```
2. Free Monad化
```haskell
type Workflow = Free WorkflowF
```
3. DSLによる業務フロー記述
```haskell
exampleFlow :: Workflow ()
exampleFlow = do
input <- liftF $ ReceiveWebhook "order_created" id
liftF $ CallHttpAPI "POST" "/save-order" ()
liftF $ NotifySlack ("Order received: " ++ input) ()
```
「何をするか」を定義しているだけで、「どうやって実行するかは定義していない。
### インタプリタ(n8n JSONへの変換器)
```haskell
interpretToN8n :: Workflow a -> [N8nNode]
interpretToN8n = go 1
where
go _ (Pure _) = []
go n (Free (ReceiveWebhook name next)) =
N8nNode "Webhook" name n : go (n+1) (next "{{$json.body}}")
go n (Free (CallHttpAPI method url next)) =
N8nNode "HTTP Request" (method ++ " " ++ url) n : go (n+1) next
go n (Free (NotifySlack msg next)) =
N8nNode "Slack" msg n : go (n+1) next
```
N8nNode は自作のデータ型で、のちに Aeson でJSONに変換。
### 出力例(JSON)
```json
{
"nodes": [
{
"type": "webhook",
"name": "order_created",
"parameters": { ... }
},
{
"type": "httpRequest",
"name": "POST /save-order",
"parameters": { ... }
},
{
"type": "slack",
"name": "Slack",
"parameters": { "text": "Order received: {{$json.body}}" }
}
],
"connections": { ... }
}
```