amber-lang / amber-lang/amber

[Feature] Functional operator for input/output redirection

オープン
#677 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る
syntax
主要言語
Rust
スター
5.2k
フォーク
145
平均マージ
5日 3時間
マージ済み PR(30日)
7

説明

This proposal aims to introduce an `IO` type for handling `stdin`, `stderr` and `stdout` as a first-class citizen in Amber, and an accompanying infix operator, `<<` which guides I/O flow from inputs to outputs, inspired by C++ syntax.

Below is an example of what that syntax would look like in action:
```js
import { stdin, stdout, stderr, pipe } from "std/io"

let my_pipe = pipe()

stdout << my_fun()
stderr << echo "hello world"
my_pipe << trust $zcat myfile.tar.gz$

// Can also be used as input
trust $tar -x$ << my_pipe
trust $cat$ << stdin

// Or chained together, in which case the resulting statement is the return value of the leftmost
// statement, unless it is a 'sink' (a file path, fd number, or the stdout/stderr/stdin builtins)
stderr << rainbow() << trust $cowsay$ << my_fun() << "./my_file.txt"
// ^~~~~~~~~ meaning this one!

// Sugar for writing to paths
"/my/file" << echo "overwrite file"
"/my/file"+ << echo "append to file"

// Or to arbitrary file descriptors
3 << echo "write to fd 3"

// Redirecting a command's standard error
"log.txt" <<2 trust $something$
let my_output = "log.txt" <<2 trust $or_other$
// Splitting stderr and stdout is expected to be handled implicitly, as a command's return value
// in Amber is its standard output.
```

And this is how it would interact with other types, using Haskell type syntax:

```haskell
-- Implicit cast: here, Cmd is the type of a bash escape expression (`$echo "this syntax"$`)
Cmd -> IO Text

-- Implicit cast: If not handled, the IO type is dropped (existing use, such as `let a = $echo b$`)
Any t => IO t -> t

-- Type interactions for the proposed `<<` I/O chain keyword:

-- Here, the left-hand operator is an IO sink (i.e. builtins `stdout` or `stderr`),
-- so we drop IO to forbid re-chaining.
(<<): Any t => (IO Null -> IO t) -> t
-- The explicit concept of an IO sink as a type may be dropped seeing as functions may bear
-- the `IO Null` type signature. Using Text and Number as below for builtins like
-- `stdout` or `stderr` would be enough. Maybe pipes would be their own type, or just the
-- path to a FIFO file under the hood.

-- Here, the left-hand operator's expression wins out if not a sink, such as chaining functions or commands together
(<<): Any t, Any u => (IO t -> IO u) -> IO t

-- This syntax allows writing into a file path:
(<<): Any t => (Text -> IO t) -> t
-- ... and reading from it:
(<<): Any t => (IO t -> Text) -> IO t

-- Same, for file descriptors:
(<<): Any t => (Number -> IO t) -> t
(<<): Any t => (IO t -> Number) -> IO t
```

Right now this type system makes no distinction between a source and a sink, merely preventing a sink (`IO Null`) from being placed in the middle of an I/O chain.

Whether `IO` should be specified explicitly in function return types, or be implied for all functions is undecided.

_Originally posted in [#235](https://github.com/amber-lang/amber/issues/235#issuecomment-2647442887)_

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。