/pr/ – programming


023a8023a8623a27042228f6b721cd7892454 – ``Божественный язык''

@2f650ed6740e458086b7c948c599e8b1 Anonymous 2021-04-10 21:10:24
@e65d8@e65d8e7067094b8589055a0446738e81
@01185@011859c4bc46423dae1be30f6868fde6
@f60560dbee0d44d3aa7b24b619bd4b56 Anonymous 2021-04-10 22:12:20
Очень удобные фичи, которые обязательно должны быть:
- именованные аргументы
- опциональные аргументы
- возможность не писать скобки в условии if
- dot нотация: person.age, "asdf".len()
- возможность писать return для раннего выхода из функции
- возможность не писать return в конце функции
- интерполяция строк: "hello {username}"
- локальный вывод типов
- деструктурирование, паттерн матчинг
- возможность создавать инфиксные функции
- встроенный to-string() для всего и разумная его дефолтная имлементация для структур данных
@03913b344e374686a1f88e4a57c07202 Anonymous 2021-04-10 22:15:30
fn ключевое слово очевидно лучше fun, func, function, lambda, \. Читаемость не страдает, удобство написания - почти максимальное, уступает только односимвольному, криповому, не читаемомум \.
@e1e9ef2fb26842abb71dcf2361cb5d68 Anonymous 2021-04-10 22:25:43
Вот это:
foo(string name)
очевидно лучше вот этого:
foo(name: string)
Потому что читаемость не страдает, а дополнительный символ (для которого ещё надо и shift требуется) печатать не нужно.
@d971a314d78541d58a16b4bd92609c5d Anonymous 2021-04-10 22:31:55
С другой стороны, string name менее удобчно чем name string. Потому что когда я пишу функцию, у меня в голове представление об аргументе первично, а его тип - вторичен.
@d7054ad5812347429f4faa99e3025647 Anonymous 2021-04-10 22:43:08
Ещё_один.txt

Но всё равно пили. Лишним оно точно не будет.
@f941bf80ae1842d7bc9dab1de07790c6 Anonymous 2021-04-10 22:45:25
Dot нотация вынуждает вызывать функции вот так: f(n). Потому что если они будут вызываться без скобок, то получится неоднознаная ситуация: x.f n.foo - непонятно к чему foo относится и это очень очень плохо читается. Её можно исправить добавив скобки: x.(f n).foo, но это неудобно писать, да и читаемость тоже страдает.
@ed63140fba8d4c14a80256ffed04eb63 Anonymous 2021-04-10 22:46:40
@d7054@d7054ad5812347429f4faa99e3025647 Я как минимум отличаюсь тем, что сразу говорю, что не пытаюсь покорить мир. Моим языком вряд ли кто-то будет пользоваться кроме меня, я это осознаю.
@954cb736c286422b88cab1af50fb7d23 Anonymous 2021-04-10 22:47:47
> - dot нотация: person.age, "asdf".len()
Зделой, чтоб это было эквивалентно age(person) и len("asdf").

> уступает только односимвольному, криповому, не читаемомум \.
В ML это алиас для $\lambda$ для людей без нормальной клавиатуры.

> Вот это:
> foo(string name)
> очевидно лучше вот этого:
> foo(name: string)
Пиз~

> С другой стороны, string name менее удобчно чем name string. Потому что когда я пишу функцию, у меня в голове представление об аргументе первично, а его тип - вторичен.
Фух!

Вообще, если уберёшь скобки, то у тебя получится почти Idris.
@e17ecec6f6314bb586536787d451fa70 Anonymous 2021-04-10 22:50:27
Что насчёт лямбд в виде {|x| ... }?
@a67ebd0c80c84bcab147cea809333c19 Anonymous 2021-04-10 22:55:48
@e17ec@e17ecec6f6314bb586536787d451fa70 x => x < 42 очевидно лучше. Чаще всего лямбды нужны в одноаргументном однострочном случае, поэтому в этом случае их запись должна быть максимльно удобной. Поэтому, я думаю, что нужно рассмотреть возможность писать лямбды вообще не указывая список аргументов: { x < 42 } или fn x < 42. Пока ещё думаю над тем как лучше сделать.
@39111a5865524dd8b67e7a146496bee4 Anonymous 2021-04-10 22:57:00
Так в Groovy сделано, только там не x, а it.
@3d1b71b9fa044afc99066f74910fe357 Anonymous 2021-04-10 22:57:30
В смысле, { x < 42 }.
@3de150354b4347b2a9d53b1306d47b60 Anonymous 2021-04-10 22:58:13
@39111@39111a5865524dd8b67e7a146496bee4 не, it плохо. Я хочу иметь возможность написать и { age < 18 }. Это более читаемо.
@59f1e39a44f447b188a03f90f9846873 Anonymous 2021-04-10 22:59:14
(x, y) -> x + y - это уже почти стандарт, так сделано в C# и JS.

Как вариант: (x y) -> x + y.
@00cd1803f7cd48f69f7b6a9a46d39c09 Anonymous 2021-04-10 22:59:35
@3de15@3de150354b4347b2a9d53b1306d47b60
{ x * y + z }
@59ca33a90db64f52b9bd729ad08b90ee Anonymous 2021-04-10 23:05:00
@00cd1@00cd1803f7cd48f69f7b6a9a46d39c09 да. Но пока мне таки больше нравится fn x * y + z.
parents.select(fn x.first-child).where(fn age < 18)
vs
parents.select({ x.first-child }).where({ age < 18 })
Первое лучше читается и легче пишется.
@ffb193bf9ddd40aeac368fa1dfdf0358 Anonymous 2021-04-10 23:06:54
@59ca3@59ca33a90db64f52b9bd729ad08b90ee
Второе лучше. fn выглядит как название функции, сливается с аргументами (или с телом функции, если аргументы не указаны). Без подсветки синтаксиса читать будет невозможно.
@9e7be03217be4190a5f2c12f1974d680 Anonymous 2021-04-10 23:14:11
Да, сливается. Всё-таки x => x < 42 лучше всего. Это божественная запись, ничего лучше не придумать.
@c7774944362945c892595af6415991a1 Anonymous 2021-04-10 23:17:05
@f941b@f941bf80ae1842d7bc9dab1de07790c6 Если ты сделаешь точку reverse application operator-ом и понизишь ей приоритет, то неоднозначности не будет. Вдобавок, можно будет писать так:
read_request .
req => switch req.method {
  case "GET" -> send_file "index.html"
  case "POST" -> { write_file("data", params.get("data")); "Posted!" }
}
@e0463348ac2a48b791e0a46fc7d69626 Anonymous 2021-04-10 23:23:06
Или вот так:
read_vector_tuple .
(x, y) => (y, x) .
(y, x) => matrix[y, x]
@31a5795153764f26be43aec52844b659 Anonymous 2021-04-10 23:24:22
(правда, у меня у точки получился совсем уж низкий приоритет, надо всё-таки повыше, чем у =>)
@956565e162a7451da76da1524c5902f9 Anonymous 2021-04-10 23:39:05
Нет, читаемость страдает, уже говорил. И точка не будет reverse application. Reverse application при этом будет, такая же как в F#: |>.
Охренеть, я сейчас узнал, что его в js собираются добавить https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator
@9f13015b01ef41478cf422644d1421a7 Anonymous 2021-04-10 23:44:34
@95656@956565e162a7451da76da1524c5902f9
Я сейчас понял, что никто не запрещает писать tuple.((x, y) => (y, x)) даже в этом случае.
@2066f0f6c1af45eaabba972171a81a14 Anonymous 2021-04-10 23:46:30
...если специально не реализовывать точку как expr "." id "(" args ")", конечно.
@9306bd8797eb46449afa2c9fec1ecad5 Anonymous 2021-04-10 23:46:53
Есть ещё вот такой вариант https://github.com/js-choi/proposal-hack-pipes/ Но как-то оно крипово выглядит.
@d8b27c2b285142519828c5ae34afa5ef Anonymous 2021-04-10 23:49:43
Я так понял, там в один пропосал всрали |> и нотацию для дефолтного аргумента лямбды (?).
@8c8e5a77c3144527917a23ef454d02b9 Anonymous 2021-04-10 23:50:53
@e6419b399b0e404699a0ea6545bcc120 Anonymous 2021-04-10 23:53:52
@d8b27@d8b27c2b285142519828c5ae34afa5ef да, это третий.
@358bc619700940c9af811fdf5f66f71f Anonymous 2021-04-10 23:54:31
У тебя скобки для аргументов будут обязательные?
@35d8ee17658b4a14a7ca07c854c92795 Anonymous 2021-04-10 23:55:12
Из @f941b@f941bf80ae1842d7bc9dab1de07790c6 следует, что да.
@ae29ee0609c74103a290979a3a8c1825 Anonymous 2021-04-10 23:56:30
И чтобы было консистенси при объявлении тоже будут скобки.
@614996e5faf542d680bad82dd855b659 Anonymous 2021-04-11 00:07:17
Хочу очень крутую фичу ввести, которой нет ни в одном языке (я не видел). Возможность при вызове функции переопределить значение связанной переменной.
fn f(a int, b int) int { g(a) + b }
f(42)
f(42, g = x => x)
@0b64d22923a545cb9c18b5517ad602a0 Anonymous 2021-04-11 00:10:21
Блядь, т.е.
f(42, 3)
f(42, 3, g = x => x)
@6023ab51d8e04f8c8ae60a6b91c5f744 Anonymous 2021-04-11 00:10:41
@61499@614996e5faf542d680bad82dd855b659 Такие фичи очень часто получаются спорными.
@548cb4a46c19412caee6e610edfc1238 Anonymous 2021-04-11 00:14:01
Мне эта фича очень нравится. Она избавляет от хардкода. Легче тесты писать. Функции максимально абстрактными становятся. Это божественная фича.
@dabca6f8987f4e6d94f82f7f49515db0 Anonymous 2021-04-11 00:15:29
Так, подожди. В твоём примере g должна быть определена в контексте?
@77d6d49acdd144269e379a8179dc9e26 Anonymous 2021-04-11 00:16:39
Да, я не дописал. Типа где-то в контексте (допустим перед определением f) есть определение функции g.
@b320389162774b1cafb22fdc50b262d5 Anonymous 2021-04-11 00:18:08
fn(42, 3, + = (x, y) => 0) - так тоже можно будет.
@471cdf019a914215a3ad3934d45c9da6 Anonymous 2021-04-11 00:20:16
fn(42, 3, + = *)
@ec861295b0aa4cc59f570f378747db9c Anonymous 2021-04-11 00:22:52
Интересная мысль.
@ca2c0c0c10ce41e4b54882f480b66e35 Anonymous 2021-04-11 00:23:03
Но её надо доработать.
@7d7f9eb34126477a9e41649dad632535 Anonymous 2021-04-11 00:23:50
Во-первых, пахнет проблемами манки-патчинга.
@290e97b50c0d4f0f94f97a28f20aafd0 Anonymous 2021-04-11 00:27:35
Да, тебе надо как-то ограничить возможность переопределения, иначе будет нарушен принцип "открыт-закрыт". Например, разрабы подсмотрел, что в твоей sqrt(x float) версии 0.01 используется функция fast_shift и решил написать свой код так: sqrt(x float, fast_shift = ultra_fast_shift). Ты обнаруживаешь баг в sqrt(x) и переписываешь её полностью. Часть клиентского кода ломается.
@d78b937ecd5c45daa8787ef8d3de5ee2 Anonymous 2021-04-11 00:29:44
Если бы не было требования объявленности этих функций в контексте, то эта фича была бы аналогична неявным аргументам с дефолтным значением.
@de4decb6028e4a2ea2805509b4cadd98 Anonymous 2021-04-11 00:30:19
Можно внутри функции писать что-то вроде using +.
@67f5d80dd4b2409eb038f5ace705f250 Anonymous 2021-04-11 00:55:11
@290e9@290e97b50c0d4f0f94f97a28f20aafd0 Это проблему можно решить настройками экспорта:
export sqrt; - можно переопределять fast_shift
export sealed sqrt; - нельзя
@8b4f87538fb347b49363b6a234f761b9 Anonymous 2021-04-11 00:58:33
То есть, можно разрешить или переопределять всё в sqrt, либо ничего?
@b99b68b202714e7c9f0c45c4ede0fb45 Anonymous 2021-04-11 01:04:45
Просто если их помечать, как ты предлаешь через using, то это не легче чем добавить в список опциональные аргументы:
fn sqrt(x float, =fast-shift)
где =fast-shift это сокращение fast-shift = fast-shift, т.е. опциональный аргумент.
@813384f82fb64caba2d6282764f113f0 Anonymous 2021-04-11 01:05:46
Да.
@c192613190dc4198b6425a1d9a21fcfd Anonymous 2021-04-11 01:07:40
Мне просто трудно представить, зачем может понадобиться экспортировать функцию со всеми переопределяемыми биндингами. Вернее, я могу представить, но это очень уж частные случаи получаются. Скорее, надо сделать export sealed по умолчанию:
export sqrt;
export customizable sqrt;
@9c4bbcf0e3bc4027ade6a4f540f8eb52 Anonymous 2021-04-11 01:15:16
Так. Строки с интерполяциями, целые беззнаковые числа и идентификаторы я уже парсю.
@5a3df9a2ea03456aa1fe9aef4ca8d94f Anonymous 2021-04-11 01:15:16
Так. Строки с интерполяциями, целые беззнаковые числа и идентификаторы я уже парсю.
@3bd4dccb6b084b7fb0fd501c7739f4d8 Anonymous 2021-04-11 01:17:53
@c1926@c192613190dc4198b6425a1d9a21fcfd можно сделать разграничение как в rust: есть pub, а есть pub(crate). B короче если pub, то sealed, а если pub(crate), то customizable.
@e93ad8fc37294236be6cdbb89200d732 Anonymous 2021-04-11 01:24:41
В лямбдах левая часть - это просто идентификаторы или это паттерн?
@ebc5c1e704fe47de93a3e45eac0aa4a9 Anonymous 2021-04-11 02:00:30
Что-то с интерполяциями беда.
@769c3238377c48b49023cbbd9e7aa166 Anonymous 2021-04-11 02:03:48
@e93ad@e93ad8fc37294236be6cdbb89200d732 Не уверен пока, думаю второе. Но будет неоднозначность: (x, y) => x + y - либо тапл, либо два аргумента. Можно конечно это решить просто добавлением ещё одних скобок: ((x, y)) => x + y, но как-то это убого выглядит по сравнению с t => t.x + t.y.
@104f902a34924e6998323e02a2755440 Anonymous 2021-04-11 02:07:58
Да норм, не так уж часто таплы деконструируются в лямбдах.
@8d0e789c37044e9cb6842ebdd24c0b51 Anonymous 2021-04-11 02:08:12
Для таких вещей есть функции curry/uncurry.
@a341e5ff3b754180ad3940809bb995b5 Anonymous 2021-04-11 02:11:22
Кебаб кейс должен быть разрешен. Он конечно немного проигрывает в читаемости снейк кейсу, но - набрать требует много меньше усилий чем _, а читаемость практически такая же. Поэтому кебаб кейс божественен. Из-за вычитания лишать язык кебаб кейса - это безумие.
@0c8a116635a14e3dbd26e9459dff7b6e Anonymous 2021-04-11 02:13:58
Что за кебаб-кейс?
@d06045638f3047afa4ebca8968c8b5b5 Anonymous 2021-04-11 02:16:29
$ runghc Syntax.hs (a => 10) + (a => 20)
Right (FunctionCall "+" [Lambda ["a"] (Number "10"),Lambda ["a"] (Number "20")])
@02d3f65962ad473eb728457c28f7f62c Anonymous 2021-04-11 02:23:26
@0c8a1@0c8a116635a14e3dbd26e9459dff7b6e лол. Если не шутишь, то это способ именования переменных через дефис: descriptive-variable-name, кебаб-кейс.
@084da328e9cf4c4f83805f5a4650a122 Anonymous 2021-04-11 02:24:01
Не слышал такое название.
@2c16355dc6f547eabff340d70f020d63 Anonymous 2021-04-11 02:24:58
my-new-function(1, minus = (x, y) => x - y)
Right (FunctionCall "my-new-function" [Arg (Number "1"),NamedArg "minus" (Lambda ["x","y"] (FunctionCall "-" [Arg (FunctionCall "x" []),Arg (FunctionCall "y" [])]))])
@499e9889093d4641b36649424147ada3 Anonymous 2021-04-11 02:28:47
"Value is {my-new-function(10, minus = (x, y) => x - y)}."
Right (Concatenation (Concatenation (String "Value is ") (FunctionCall "my-new-function" [Arg (Number "10"),NamedArg "minus" (Lambda ["x","y"] (FunctionCall "-" [Arg (FunctionCall "x" []),Arg (FunctionCall "y" [])]))])) (String "."))
@713851f214e048f29a463bc3d9cef39f Anonymous 2021-04-11 02:31:12
{abc; def}
Right (Sequential [FunctionCall "abc" [],FunctionCall "def" []])
@47fef5a4df1a4ee2b3e6f08107767e97 Anonymous 2021-04-11 02:32:02
{abc def}
Right (Sequential [FunctionCall "abc" [],FunctionCall "def" []])
@95a3d2ad994345259b3bf0b5083b6d10 Anonymous 2021-04-11 02:36:44
@f6056@f60560dbee0d44d3aa7b24b619bd4b56
Ещё есть важная фича, которая повышает читаемость кода. Видел её в swift. Function Argument Labels and Parameter Names
fn greet(person string, from hometown String)
greet(person: "Ivan", from: "Brazil")
Есть ещё такой вариант:
fn greet(person string)from(hometown String)
greet("Ivan")from("Brazil")
Но его очень легко спутать с вызовом двух функций при беглом чтении, если не заметить отсутствие точки, поэтому он не годится.
@2803b19a651e412ba34b42c403b9dd82 Anonymous 2021-04-11 02:53:28
f(1, +: (x, y) => x - y)
Right (FunctionCall "f" [Arg (Number "1"),NamedArg "+" (Lambda ["x","y"] (FunctionCall "-" [Arg (FunctionCall "x" []),Arg (FunctionCall "y" [])]))])


f(1, +: -)
Right (FunctionCall "f" [Arg (Number "1"),NamedArg "+" (FunctionCall "-" [])])
@364934a4a1f44a29a9b109c1cf3c298f Anonymous 2021-04-11 02:58:40
@95a3d@95a3d2ad994345259b3bf0b5083b6d10
fn greet(person string, from hometown string)
greet(person = "Ivan", from = "Brazil")
fix
@98055d4df088444ba1ca886dbaeb7a9d Anonymous 2021-04-11 03:08:26
Ещё важная фича, которая упрощает написание и правку кода и при этом не ухудщает читаемость - это trailing commas.
@5d76d9940d894f6cb1b1a2255b5d3dec Anonymous 2021-04-11 03:13:25
Это a; b;;;;?
@2956720eaf0a4db4a41e61e9874386b6 Anonymous 2021-04-11 03:13:33
Ой, не то.
@8a76c60e2d6441949e6b9973cb28a1ee Anonymous 2021-04-11 03:13:42
А, понял.
@3ab093fdde7d43159d4d74aa9f8e7506 Anonymous 2021-04-11 03:17:21
fn hello(name String, from hometown String) { "Hello {name} from {hometown}!" }  hello("John", from="Brazilia")

Right [FunctionDeclaration "hello" [ArgumentSpecification {argName = "name", argLabel = Nothing, argType = FunctionCall "String" []},ArgumentSpecification {argName = "from", argLabel = Just "hometown", argType = FunctionCall "String" []}] (Sequential [Statement (Concatenation (Concatenation (Concatenation (Concatenation (String "Hello ") (FunctionCall "name" [])) (String " from ")) (FunctionCall "hometown" [])) (String "!"))]),Statement (FunctionCall "hello" [Arg (String "John"),NamedArg "from" (String "Brazilia")])]
@793456d8b80c490682bba58d19bc6060 Anonymous 2021-04-11 03:20:21
fn >^<(> V, < ^ +) { >(^) }   >^<("WTF", < = "IS THIS SHIT")

Right [FunctionDeclaration ">^<" [ArgumentSpecification {argName = ">", argLabel = Nothing, argType = FunctionCall "V" []},ArgumentSpecification {argName = "<", argLabel = Just "^", argType = FunctionCall "+" []}] (Sequential [Statement (FunctionCall ">" [Arg (FunctionCall "^" [])])]),Statement (FunctionCall ">^<" [Arg (String "WTF"),NamedArg "<" (String "IS THIS SHIT")])]
@5ba867d6ccb54869aaa1f37fce8d15d0 Anonymous 2021-04-11 03:21:41
Для trailing commas надо свой комбинатор писать.
@1dbf0e30ebac4391919deeb4d8cf5cb2 Anonymous 2021-04-11 03:38:06
Язык должен быть богат на литералы. Должны быть литералы для списков, таплов, словарей, сетов, регулярок.
{
	first-name = 'John',
	last-name = 'Doe',
	age = 1761,
	is-alive = true,
	children = [
		{
			first-name = 'Stephani',
			age = 144,
			is-alive = false,
		},
		{
			first-name = 'Kate',
			age = 566,
			is-alive = true,
		},
	],
}
@dfaf49ba3c3a41119aae2aa310eebfeb Anonymous 2021-04-11 03:39:27
Кстати:
fn map(people Array(Person), f Function(Person, Int)) { TODO }

Right [FunctionDeclaration "map" [ArgumentSpecification {argName = "people", argLabel = Just "Array", argType = FunctionCall "Person" []},ArgumentSpecification {argName = "f", argLabel = Nothing, argType = FunctionCall "Function" [Arg (FunctionCall "Person" []),Arg (FunctionCall "Int" [])]}] (Sequential [Statement (FunctionCall "TODO" [])])]
@afd3777c9d004f07a66569db19924d52 Anonymous 2021-04-11 03:41:16
(кажется, я лямбды испортил)
@9739e52f510742f784f6f98549b8a5eb Anonymous 2021-04-11 03:45:21
db.where((person) => person.age > 18).select((person) => person.salary).reduce(+)

Right [Statement (FunctionCall "." [Arg (FunctionCall "." [Arg (FunctionCall "." [Arg (FunctionCall "db" []),Arg (FunctionCall "where" [Arg (Lambda ["person"] (FunctionCall ">" [Arg (FunctionCall "." [Arg (FunctionCall "person" []),Arg (FunctionCall "age" [])]),Arg (Number "18")]))])]),Arg (FunctionCall "select" [Arg (Lambda ["person"] (FunctionCall "." [Arg (FunctionCall "person" []),Arg (FunctionCall "salary" [])]))])]),Arg (FunctionCall "reduce" [Arg (FunctionCall "+" [])])])]
@d9bb21536cb6495b8760018012a18f6f Anonymous 2021-04-11 03:47:25
>.< (CYKA, BLEAT)

Right [Statement (FunctionCall "." [Arg (FunctionCall ">" []),Arg (FunctionCall "<" [Arg (FunctionCall "CYKA" []),Arg (FunctionCall "BLEAT" [])])])]
@4951e5afbcd44ede87ccd66f69c8227a Anonymous 2021-04-11 03:54:50
Быдлокод:

Syntax.hs:

{-# LANGUAGE RecordWildCards #-}
import Text.Parsec hiding (string, token)
import qualified Text.Parsec as P
import Text.Parsec.Expr
import Control.Monad

program = nonsignificant *> many statement <* eof

-- * Statement

data Statement
    = FunctionDeclaration Identifier [ArgumentSpecification] Expr
    | Statement Expr
    deriving (Show)

data ArgumentSpecification = ArgumentSpecification
    { argName :: Identifier
    , argLabel :: Maybe Identifier
    , argType :: Expr
    }
    deriving (Show)

statement = do
    try fundecl
    <|> try (expr >>= return . Statement)

fundecl = do
    str "fn"
    n <- identifier
    args <- option [] $ do
        symbol '(' *> (try labeledArg <|> try arg) `sepBy` symbol ',' <* symbol ')'
    b <- sequential
    return $ FunctionDeclaration n args b
  where
    labeledArg = do
        argName <- identifier
        argLabel <- return . Just =<< identifier
        argType <- expr
        return $ ArgumentSpecification { .. }
    arg = do
        argName <- identifier
        argType <- expr
        return $ ArgumentSpecification { argLabel = Nothing, .. }

-- * Expression

-- TODO: Trailing commas. Maybe a combinator similar to sepBy?

data Expr
    = Concatenation Expr Expr
    | String [Char]
    | Number [Char]
    | FunctionCall Identifier [Arg]
    | Lambda [Identifier] Expr
    | Sequential [Statement]
    deriving (Show)

data Arg
    = Arg Expr
    | NamedArg Identifier Expr
    deriving (Show)

expr =
    buildExpressionParser
    [ [Infix (binary ".") AssocLeft ]
    , [Infix (binary "*") AssocLeft, Infix (binary "/") AssocLeft]
    , [Infix (binary "+") AssocLeft, Infix (binary "-") AssocLeft]
    , map (flip Infix AssocLeft . binary) ["<", ">", "<=", ">="]
    , [Prefix $ try lambda]
    ]
    term
  where
    binary name = str name *> return (\x y -> FunctionCall name $ map Arg [x, y])
    lambda = do
        args <-
            try (identifier >>= return . (:[]))
            <|> try (symbol '(' *> (identifier `sepBy` symbol ',') <* symbol ')')
        str "=>"
        return $ Lambda args

term =
    try funcall
    <|> try string
    <|> try number
    <|> try sequential
    <|> try bracketed

sequential =
    symbol '{' *> (statement <* mbSemicolon) `sepBy` nonsignificant <* symbol '}' >>= return . Sequential
  where
    mbSemicolon = optional $ symbol ';'

bracketed = symbol '(' *> expr <* symbol ')'

funcall = do
    n <- identifier
    args <- option [] $ do
        symbol '(' *> (try namedArg <|> arg) `sepBy` symbol ',' <* symbol ')'
    return $ FunctionCall n args
  where
    arg = expr >>= return . Arg
    namedArg = do
        n <- identifier
        symbol '='
        e <- expr
        return $ NamedArg n e

-- * String (which is a cross between an expression and a lexeme)

string = token $ do
    char '"'
    s <- chainl part (return $ \x y -> Concatenation x y) (String "")
    char '"'
    return s
  where
    part =
        try (char '\\' *> anyChar >>= return . String . (:[]))
        <|> try (char '{' *> nonsignificant *> expr <* char '}')
        <|> try (many1 (noneOf ['{', '"']) >>= return . String)

-- * Lexis

number = token $ many1 digit >>= return . Number

identifier = token $
    try name
    <|> try operatorIdentifier
  where
    name = do
        a <- oneOf start
        b <- many (oneOf $ start ++ ['-', '0'..'9'])
        return $ [a] ++ b
      where
        start = ['_'] ++ ['a'..'z'] ++ ['A'..'Z']
    operatorIdentifier =
        many1 $ oneOf "+-*/<>^&|~"

type Identifier = [Char]

nonsignificant = skipMany (space) -- TODO: comments

-- * Utilities

token t = t <* nonsignificant

symbol c = token $ char c

str s = token $ P.string s

-- * Main

main = do
    c <- return . parse program "-" =<< getContents
    putStrLn $ show c
@d83e113a170a4b6a85245fd1cbf0adfe Anonymous 2021-04-11 04:06:28
Сразу говорю, что с опциональными аргументами ты отказываешься от автокаррирования.
@f1c4249148e740e38e747b7ecf2f9b5b Anonymous 2021-04-11 04:24:55
В Scala каррируемые аргументы помечаются символом _. Это ещё и без flip-ов позволяет обходиться.
@0c83da3d2990425d8b5dd32623cc80a5 Anonymous 2021-04-11 05:05:25
Вообще надо лексику сначала продумать, потом уже всё остальное.
@ab47560667744cf6b8fd7e1e3a202f38 Anonymous 2021-04-11 12:12:05
@d83e1@d83e113a170a4b6a85245fd1cbf0adfe Им можно пожертвовать.
@9878b4bcbf6a4fcab31bfb9351d93510 Anonymous 2021-04-11 12:41:49
Объявление переменных.
let x = 42;
vs
var x = 42;
vs
const x = 42;
vs
x := 42;
vs
x = 42;
vs
x <- 42;
Тут однозначно let и var выигрывают. У конст они выигрывают сразу и в читаемости и удобстве написания. У := и = они выигрывают в читаемости т.к. визуально выделаются и поэтому легче находить объявление переменных при чтении, их не спутаешь с присваиванием. := выглядит крипово для человека не знакомого с паскалем. <- тоже слегка припово.
И чисто эстетически мне let больше нравится чем var. Ну и Моисей говорил "Let my people go", поэтому let божественен.
@7a44a5d7ff1a407cab84c2c8de63d365 Anonymous 2021-04-11 12:48:39
let важен больше не как присваивание, а как обозначение места в программе где переменная впервые появляется.
@1a43542a3eee4c07b45ea4f6008e6b35 Anonymous 2021-04-11 12:57:48
(relinking) @9878b@9878b4bcbf6a4fcab31bfb9351d93510 @4d128@4d1282b4cb174f8593c6204969a50329
@1ac8aeab4fa842e9a3c5c36ebfa60040 Anonymous 2021-04-11 12:57:57
let mut user = get-user();
user.is-blocked = true;
save(user);
Возможность мутабельности нужна. Она может облегчать написание кода и улучшать читаемость.
@0d867d4c8bb749b080d447b2cadc06a1 Anonymous 2021-04-11 12:58:30
Вместо let mut можно использовать var (как в JS).
@ba76e40b509d4ab59e277a3da15a6942 Anonymous 2021-04-11 12:59:05
let mut := get-user();

Вот так. Иначе я приеду к тебе домой и нассу под дверь.
@7f7041e1c6c248fdac1ab55fc4832131 Anonymous 2021-04-11 12:59:51
Как интерполяции парсить?
@ede5efa1d7044834a566289b9fb54d20 Anonymous 2021-04-11 13:01:09
@0d867@0d867d4c8bb749b080d447b2cadc06a1 Там const для иммутабельных. let mut лучше.
@2382c0f9c6644bc1a763daae932af916 Anonymous 2021-04-11 13:03:12
А. В JS вообще всё через жопу.

Но где-то я видел именно let и var...
@a526b762404a419dbef2ca316189585a Anonymous 2021-04-11 13:14:17
@ba76e@ba76e40b509d4ab59e277a3da15a6942 Это масло масляное. Ради того чтобы проверку на равенство писать как x = 42 вместо x == 42?
x == 42 прекрасно читается, это уже стандарт. Ошибки когда написал x = 42 вместо x == 42 у меня были крайне редко, только когда начинал прогать. И если запретить в языке писать x = 42 во всяких if и прочих экспрешенах, то таких ошибок не будет.
@f0f8a31c5a474afba6f72ebd14ae5bb2 Anonymous 2021-04-11 13:17:57
К тому же, проверка на равенство обычно в коде встречается значительно реже присваивания/объявления с присваиванием.
@ac991b8d9f054702913f1b0079c3267a Anonymous 2021-04-11 13:18:15
Начинается.
@e52a1d0ed806464e97b23650852dee99 Anonymous 2021-04-11 13:18:29
Ничему история людей не учит. Ну да и хрен с ним.