Буквально несколько дней назад компания Microsoft представила публике новый язык программирования. Языку дали название Bosque. Главная миссия дизайна языка — лучше быть богатым и здоровым, чем бедным и больным чтобы он был прост и понятен как для человека, так и для компьютера.
Информации пока что очень мало, язык еще очень свежий и нестабильный. Есть лишь paper от Марка Марона и дока в начальной стадии написания.
Давайте попробуем рассмотреть некоторые особенности языка.
1) Все значения в Bosque являются неизменяемыми (immutable), чтобы каждый блок кода не содержал никаких сайд-эффектов. По мнению авторов языка это полезно как для человека, так и для компьютера.
При этом, как ни странно, чуть далее по тексту рассказывается, что можно объявить изменяемую переменную ключевым словом var!
. И это не просто сахар для более удобной инициализации иммутабельной переменной, это действительно настоящая переменная.
Ладно, допустим, это некий компромисс, и в локальном скоупе так сделали. Но дальше идет упоминание о еще не реализованной фиче языка — передаче аргументов функции по ссылке
function internString(ref env: Map<String, Int>, str: String): Int {
if(env.has(str)) { //use the ref parameter
return env.get(str);
}
env = env.add(str, env.size()); //update the ref parameter
return env.size();
}
Может я чего-то не понимаю, но странная какая-то иммутабельность в языке, если мы можем передать Map по ссылке, и фунция ее намутирует.
2) В языке нет циклов for, while и т.д. Вообще никаких нет. Вместо этого есть коллекции и пайплайны. Другими словами, вместо циклов нужно использовать более высокоуровневые штуки типа map, filter и т.д.
3) Строки можно делать разных типов. Т.е., например, можно сделать строку-имя или строку-zipcode, и для type-чекера это будут две разные строки. Если вы в аргументе функции ожидаете zipcode, а вам по ошибке туда пихают имя, то компилятор это не проглотит. Синтаксис такой: String[Zipcode].
Тоже сомнительная штука, почему не просто ZipCode. Один раз объявить, что ZipCode — это строка и везде в сигнатурах это писать. И почему именно для строк эта типизация сделана, а не для интов, например.
4) Вызов функций можно делать с указанием названия аргументов из сигнатуры функции, например: myfunc(x=1, y=2)
5) В стандартной библиотеке есть различные коллекции, и с коллекциями можно работать по разному. Можно просто по цепочке вызывать map, потом filter и т.д., а можно работать через пайплайны.
var v: List[Int?] = List@{1, 2, none, 4};
//Chained - List@{1, 4, 16}
v->filter(fn(x) => x != none)->map[Int](fn(x) => x*x)
//Piped none filter - List@{1, 4, 16}
v |> filter(fn(x) => x != none) |> map[Int](fn(x) => x*x)
//Piped with noneable filter - List@{1, 4, 16}
v |??> map[Int](fn(x) => x*x)
//Piped with none to result - List@{1, 4, none, 16}
v |?> map[Int](fn(x) => x*x)
6) рекурсия считается злом, которое может усложнить программу, поэтому рекурсивные фунции надо помечать словом rec
7) программы на Bosque являются детерминированными. Другими словами в языке нет неопределенного поведения. Например, нельзя использовать переменные, пока они не были определены; алгоритмы сортировки только стабильные и т.д. Если программа выдала какой-то результат, то такой же результат будет и потом, никаких сюрпризов
8) Вместо классов и интерфейсов в языке есть понятия entity и concept.
concept Bar {
field f: Int;
}
entity Baz provides Bar {
field g: Int;
field h: Bool = true;
}
var y = Baz@{f=1, g=2, h=false}; //Create a Baz entity with the given field values
var x = Baz@{f=1, g=2}; //Create a Baz entity with default value for h
Пример кода
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
//
//This is a bosque test/benchmark for a tic-tac-toe program.
//
namespace NSMain;
entity Board {
const playerX: String[PlayerMark] = 'x'#PlayerMark;
const playerO: String[PlayerMark] = 'o'#PlayerMark;
const allCellPositions: List[[Int, Int]] = List[[Int, Int]]@{
@[ 0, 0 ], @[ 1, 0 ], @[ 2, 0 ],
@[ 0, 1 ], @[ 1, 1 ], @[ 2, 1 ],
@[ 0, 2 ], @[ 1, 2 ], @[ 2, 2 ]
};
const winPositionOptions: List[List[[Int, Int]]] = List[List[[Int, Int]]]@{
List[[Int, Int]]@{ @[ 0, 0 ], @[ 0, 1 ], @[ 0, 2 ] },
List[[Int, Int]]@{ @[ 0, 1 ], @[ 1, 1 ], @[ 2, 1 ] },
List[[Int, Int]]@{ @[ 0, 2 ], @[ 1, 2 ], @[ 2, 2 ] },
List[[Int, Int]]@{ @[ 0, 0 ], @[ 1, 0 ], @[ 2, 0 ] },
List[[Int, Int]]@{ @[ 1, 0 ], @[ 1, 1 ], @[ 1, 2 ] },
List[[Int, Int]]@{ @[ 2, 0 ], @[ 2, 1 ], @[ 2, 2 ] },
List[[Int, Int]]@{ @[ 0, 0 ], @[ 1, 1 ], @[ 2, 2 ] },
List[[Int, Int]]@{ @[ 0, 2 ], @[ 1, 1 ], @[ 2, 0 ] }
};
//Board is a list of marks, indexed by x,y coords from upper left 0 based
field cells: List[String[PlayerMark]?];
factory static createInitialBoard(): { cells: List[String[PlayerMark]?] } {
return @{ cells=List[String[PlayerMark]?]::createOfSize(9, none) };
}
method getOpenCells(): List[[Int, Int]] {
return Board::allCellPositions->filter(fn(pos: [Int, Int]): Bool => {
return !this->isCellOccupied(pos[0], pos[1]);
});
}
method getCellContents(x: Int, y: Int): String[PlayerMark]?
requires 0 <= x && x < 3 && 0 <= y && y < 3;
{
return this.cells->at(x + y * 3);
}
method isCellOccupied(x: Int, y: Int): Bool {
return this->getCellContents(x, y) != none;
}
method isCellOccupiedWith(x: Int, y: Int, mark: String[PlayerMark]): Bool
requires mark == Board::playerX || mark == Board::playerO;
{
return this->getCellContents(x, y) == mark;
}
method markCellWith(x: Int, y: Int, mark: String[PlayerMark]): Board
requires mark == Board::playerX || mark == Board::playerO;
requires 0 <= x && x < 3 && 0 <= y && y < 3;
requires !this->isCellOccupied(x, y);
{
return this<~(cells=this.cells->set(x + y * 3, mark));
}
hidden method checkSingleWinOption(opt: List[[Int, Int]], mark: String[PlayerMark]): Bool {
return opt->all(fn(entry: [Int, Int]): Bool => this->isCellOccupiedWith(entry[0], entry[1], mark));
}
hidden method checkSingleWinner(mark: String[PlayerMark]): Bool {
return Board::winPositionOptions->any(fn(opt: List[[Int, Int]]): Bool => this->checkSingleWinOption(opt, mark));
}
method checkForWinner(): String[PlayerMark]? {
if(this->checkSingleWinner(Board::playerX)) {
return Board::playerX;
}
elif(this->checkSingleWinner(Board::playerO)) {
return Board::playerO;
}
else {
return none;
}
}
}
entity Game {
field winner: String[PlayerMark]? = none;
field board: Board = Board@createInitialBoard();
method hasWinner(): Bool {
return this.winner != none;
}
method getWinner(): String[PlayerMark]
requires this->hasWinner();
{
return this.winner->as[String[PlayerMark]]();
}
method makeAutoMove(mark: String[PlayerMark], rnd: Int): Game
requires !this->hasWinner();
{
var! nboard: Board;
if(!this.board->isCellOccupied(1, 1)) {
nboard = this.board->markCellWith(1, 1, mark);
}
else {
var opts = this.board->getOpenCells();
var tup = opts->uniform(rnd);
nboard = this.board->markCellWith(...tup, mark);
}
return this<~( board=nboard, winner=nboard->checkForWinner() );
}
method makeExplicitMove(x: Int, y: Int, mark: String[PlayerMark]): Game
requires !this.board->isCellOccupied(x, y);
{
var nboard = this.board->markCellWith(x, y, mark);
return this<~( board=nboard, winner=nboard->checkForWinner() );
}
}
entity PlayerMark provides Parsable {
field mark: String;
override static tryParse(str: String): PlayerMark | None {
return (str == "x" || str == "o") ? PlayerMark@{ mark=str } : none;
}
}
entrypoint function main(): Game {
var! game = Game@{};
game = game->makeAutoMove(Board::playerX, 0);
game = game->makeAutoMove(Board::playerO, 1);
game = game->makeAutoMove(Board::playerX, 2);
game = game->makeExplicitMove(2, 0, Board::playerO);
game = game->makeExplicitMove(2, 1, Board::playerX);
return game;
}
Итого
В общем, желаю языку удачи, конечно, но пока что Bosque выглядит странновато и сыровато. Скорее proof of concept. Причем, не совсем ясно, что именно является киллер-фичей. Документация порождает больше вопросов, чем ответов.
Компилятор для Bosque написан на Typescript, а не на самом языке, как это принято (например, компилятор Typescript написан на Typescript). Т.е. язык, скорее всего, еще недостаточно развит для написания более менее сложных программ.
В ближайшем выпуске подкаста "Цинковый прод" мы обязательно обсудим новый язык Bosque, возможно удастся собрать побольше информации. Так что не забудьте подписаться.
Anton23
Может мне кто нибудь объяснить, почему компилятор для яп пишется на том же яп? На сколько я знаю, первый компилятор для TS был написан не на TS,
и был написан только с одной целью — скомпилировать компилятор на TS.Так вот — это делается по идеологическим причинам или по каким то другим?varanio Автор
Во-первых, это традиция. Во-вторых, на новом языке нужно что-то написать, какую-то реальную программу, чтобы отладить все нюансы. Можно выдумывать абстрактную программу в вакууме, а можно начать с реальной — с компилятора.
Но вообще, конечно, это необязательно для языка. Просто хорошая традиция
Tangeman
Если это традиция, то где же компиляторы (или хотя бы интерпретаторы) Perl на Perl, PHP на PHP, Ruby на Ruby или Python на Python?
Чтобы отладить все нюансы есть достаточно реальных задач кроме собственно компилятора, в то время как написание компилятора не обязательно затронет все нюансы которые нужно отлаживать, но вот времени займёт просто уйму (особенно если язык сильно отличается от того на котором пишут первый компилятор).
Задачи, к тому же, необязательно должны быть такими сложными во время разработки языка — к примеру, для отладки рекурсии хватит чисел Фибоначчи, для отладки многопоточности или асинхронной обработки — web-client/server.
Но разумеется, если у разработчиков свободного времени вайлом — тогда да, почему бы и нет, собственно, пишут же эмулятор Z80 на bash…
splav_asv
Ну, есть PyPy, есть PHPPHP.
Для интерпретируемых языков больше характерна реализация на более низкоуровневом языке (C или C++, как правило). Или наличие трансляторов в C/Javascript.
Deathik
Так вы перечислили не компилируемые ЯП, если что. Они не самые быстрые, потому интерпретатор, как по мне, на том же языке писать — профита мало Да, за остальных не скажу, но есть у Питона — PyPy, можете почитать, ради интереса
gatoazul
Компилятор Perl6 написан на нем же.
MechanicZelenyy
Компилятор Python на Python, это уже упомянутый PyPy, который помимо того что следует традиции, ещё и предоставляет jit-оптимизации, и ускоряет программу в несколько раз.
gudvinr
Формально, PyPy — это не компилятор Python на Python.
PyPy — это реализация интерпретатора Python на подмножестве языка RPython (валидный код на Python не обязательно является валидным для RPython).
Сам PyPy написан на RPython, который уже является компилируемым языком. Но при этом компилятор RPython уже по большей части написан на Python.
Aingis
Картинка просто напрашивается:
SeyranGV
<шутка>а вы не задумывались над тем что эти люди просто научились писать в совершенстве компиляторы и больше не с чем заморачиваться не хотят <\шутка>
nmrulin
PHP поэтому и остался ограниченным языком для интернета. Компилируемые программы на нём никто не пишет. Питон тоже крайне медленный язык(правда есть быстрые реализации, но всё же). Так что ваше примеры только подтверждают правило, что нет полноценных языков, которые себя компилировать не могут.
a0fs
Компилятор — программа переводящая нечто написанное на неком языке в форму пригодную для исполнения на неком устройстве.
— Машина работает с маш. кодами операций и аргументами в виде ячеек памяти и регистров.
— Асемблер даёт имена маш. кодам и регистрам, делая их человекочитаемыми и наворачивает немного синтаксического сахара по оформлению операций. Но всё это приводимо к маш. кодам практически 1:1
— С и иные системные языки — реализуют некоторые базовые конструкции алгоритмизации в своём синтаксисе, а также организуют среду программирования, состоящую из процедур и методов их написания. Что-то из данных процедур можно написать на асемблере и, используя соглашение о вызове процедур, внести её в среду программирования.
И так далее. То есть язык более высокого уровня добавляет некоторое количество абстракций над языком более низкого, которые в основном представляют из себя просто часто повторяемые конструкции нижележащего языка приятно оформленные в некоторый синтаксис. Когда компилятор языка пишется на самом компилируемом языке возникает ощущение, что языка в этом месте быть и не должно. Возможно нужно спуститься ниже и переписать язык более низкого уровня. Компилятор может частично быть написан на своём языке, но ядро компилятора должно быть написано на языке, в который данный компилятор программу и перегоняет… По другому смысла в компиляторе не видно.
И вообще это языковое безумие заставляет всё чаще вспоминать историю о Вавилонской башне если честно…
juray
А уже в зависимости от того, выполняется ли трансляция целиком всей программы до выполнения, или по кусочку во время исполнения — получаем деление на компиляторы и интерпретаторы.
vitvakatu
Грань становится тоньше, когда вспоминаем про JIT-компиляторы, которые могут также по кусочкам в рантайме программу компилировать
juray
скорее, не тоньше, а размытее.
domix32
Вот дозахватят нас роботы, тогда и
bip bop bop bop bop bip bip bip bip bip bip bip bop bop bop bop bip bip bip bip bip bop bip bop bop bop bop bip bip bop bop bip bip bip bop bop bop bop bip bip bip bip bip bop bip bop bop bop bop bip bip bop bop bip bop bip bop bop bop bop bip bip bip bip bip bop bip bop bop bop bip bop bop bop bop bop bop bip bop bop bop bop bip bip bip bop bop bop bip bop bop bop bop bip bip bip bip bop bop
Cerberuser
А если будет восстание обезьян, тогда что?
Ook! Ook.
?domix32
Если и есть к этому предпосылки, то не в нашей вселенной.
Cryvage
matabili1973
Ну да. Если посадить миллион обезьян за пишущие машинки, то рано или поздно одна из них выиграет президентские выборы?
MTyrz
gudvinr
"Традиция" — это довольно сырой аргумент. Нужно понимать, что такие вещи делаются чтобы деньги зарабатывать напрямую, либо использовать наработки в других местах. Это ведь не Вася из 9 класса, который написал свой первый парсер и "потому что так принято" делать никто не будет.
Раскрутка компилятора позволяет разработчикам на языке, для которого создается компилятор, этот язык и развивать. Достаточно прагматичная цель, интуитивно выглядит разумным, что разработчики, которым что-то нужно от языка, будут охотнее в него вносить изменения и им не нужно будет вникать в особенности низкоуровневой разработки на втором языке (на котором написан компилятор).
Окружение уже развёрнуто, знай себе только патчи отправляй.
Правда, если язык незрелый — для него нет никаких инструментов. Для TS куча линтеров-чекеров, поддержка в редакторах, миллиарды статей и много разработчиков, поэтому в данном случае от этого нет никаких преимуществ.
Вполне возможно, что через год-полтора сам язык торжественно передадут в сообщество (читай: забьют на разработку), а наиболее интересные решения войдут в TypeScript.
splav_asv
Еще два плюса:
Пример когда другой язык реализации мешает — Java. Отчасти поэтому сейчас есть проект VM на самой Java — GraalVM.
some_x
Это называется «dogfooding».
В том смысле, что разработчики должны сами есть ту «собачью еду» что производят, чтоб лучше понимать какова она на вкус.
QtRoS
Наверное все же "dogfooding"
some_x
Да, конечно! Спасибо, исправил.
kzhyg
Это называется «раскрутка компилятора», плюсы и минусы, соответственно, можно посмотреть на Википедии.
Aquahawk
Смотрите, вот файл из недр компилятора TypeScript github.com/Microsoft/TypeScript/blob/master/src/compiler/checker.ts 1.8 Мегабайта (!) одним куском. И его нельзя отрефакторить, потому что был использован module pattern который применяется в JS но абсолютно не имеет никакого смысла в TypeScript. И этот паттерн не расширяем. В итоге мы имеем 32К строк в одном файле? И 2624 вхождения слово function (из них большинство это действительно функции, а не комменты) И 462 стрелочных функции. И он растёт, и будет расти. Ещё в 2017 году я предпринимал попытки автоматизированными средствами это отрефакторить github.com/Microsoft/TypeScript/issues/17861 Ввиду нехватки времени на разработку предложенное решение имеет недостатки и проваливает производительность (я там бинжу все функции, а это не требуется в общем случае, но правильный детектор этого писать лень).
Так вот, новые ЯП как правило нужны для введения и демонстрации новых техник программирования, ЯП никто не релизит ради синтаксиса. И есть концепция en.wikipedia.org/wiki/Eating_your_own_dog_food которая говорит что нужно использовать свой продукт. И вот вы как разработчик языка начинаете на нём писать его — же. Понимать как на самом деле работает то что вы придумали. В случае в TS изначальная архитектура пришла из старых JS подходов, и она откровенная слаба в TS мире. Вот поэтому я считаю что нужно писать ЯП на нём самом, не если это не DSL какой-нибудь.
some_x
А почему этот модуль нельзя разбить на несколько модулей поменьше + ещё один модуль который импортирует все функции из этих «чанков» и реэкспортирует их всех в виде неймспейса ts.
Aquahawk
Потому что в данном случае у этих функций огромный общий шареный стейт, что должно бы было быть свойствами объекта. Вот его часть
TheShock
Что если все эти зависимости перенести в один объект. Вроде:
Во всем коде использовать этот объект как Service Locator, а потом начать передавать всем, кто его хочет как зависимость?
Aquahawk
Так я и делал в своём рефакторинге, я в том случае все свойства на this навесил и все методы пробиндил. И это даже тесты прошло полностью. Проблема в том что код стал ещё хуже и просел перформанс. А руками это сделать можно, ничего кроме объёма работы не мешает. Но сейчас никто не готов столько работы пожертвовать.
impwx
Основная причина, имхо, в том, что невозможно оценить пригодность и эффективность языка, не написав на нем хотя бы одну сложную программу. Сделать минимальный bootstrapping на другом языке, чтобы сразу писать основной код компилятора языка на нем же самом — это одновременно красивый ход для инженеров и эффективный для бизнеса.
Кроме того, усовершенствование компилятора (добавление новых оптимизаций и проверок) помогает улучшать его собственную работу.
serge-phi
Это делается в том числе и для доказательства того, что язык тьюринг-полный.
Aquahawk
Не сказал бы. Тьюринг полноты не достаточно для удобного программирования, и в то же время есть много не тьюринг полных DSL которые удобны. Как пример тьюринг полнота системы типов typescript github.com/Microsoft/TypeScript/issues/14833 вот ещё посмотрите ru.wikipedia.org/wiki/Тьюринговская_трясина
tbl
Вроде был язык, в котором только 3 функции, первая печатает "Hello world", вторая выводит текст программы, а третья генерирует компилятор этого языка. Так что возможность написать компилятор языка на самом языке не является доказательством полноты по Тьюрингу.
TheShock
HQ9+
juray
только без генерации компилятора.
worldmind
Вот пишете вы на каком-то языке, растёте и в какой-то момент хотите в нем что-то улучшить/пофиксить и опа — оказывается что ваш 20 летний опыт разработки на этом языке тут не поможет, надо пойти ботанить какой-то другой язык на котором написан компилятор.
Aquahawk
Ну справедливости ради стоит сказать что ооочень часто средний разработчик решает задачи кратно проще чем разработка компилятора, и идея просто зайти, почитать и поправить компилятор для внесения своих фич обычно идеей и остаётся, не зависимо от того, на каком языке это написано.
worldmind
Очевидно, что это не для средних, но кто-то должен развивать язык и понятно что это должны делать те, кто им пользуются, а раз пользуются, то скилы других языков теряются в связи с ненадобностью.
7aitsev
Это называется Bootstrapping.
NeoCode
Как можно создавать язык программирования, не написав к нему документацию? Это уже не первый случай. Сначала накодят что-то, а потом задним числом сочиняют спецификацию.
Tangeman
А почему нет? Вполне может быть что авторы ещё не представляют себе что там должно быть, как оно должно работать или вообще ещё не определились чего хотят, вот и эксперементируют, заодно собирая мнения и идеи.
Разница только в том что кто-то это делает публично, а кто-то «тайно» — в первом случае об отсутствии спецификации в начале разработки известно всем, а во втором об этом может никто и не узнать (поскольку её выложат вместе с языком) — хотя принципиальной разницы нет.
В конце концов, это ж не мостостроение, где опасно начинать строить пока нет проекта и расчётов, особенно с учётом того что нигде в продакшн это не используется.
impwx
Встречный вопрос: какой смысл писать документацию к проекту в экспериментальной стадии, когда основные фичи еще обкатываются?
dollar
Называется Agile. И внезапно это удобно и эффективно, хотя и не везде.
Tzimie
Не в том ли идея чтобы он сразу мог бы параллелится?
NotThatEasy
Для упрощения этой задачи тут только иммутабельность, что само по себе не гарантирует хорошего паралеллизма.
Tzimie
Ну если нет циклов и идет работа со множествами то это напоминает SQL, который как раз располагает к паралеллизму…
NotThatEasy
Всё-таки давайте не будем смешивать в кучу язык общего назначения и SQL.
Кстати, fun fact: Я был так много наслышан о том, как Хаскелл хорошо параллелится, ещё когда начал учить первый язык, что когда я схватился за Хаскелл, был поражён, что параллелить надо через монаду, что накладывает не одно ограничение на мой код, т.к. я ещё не понял их и трансформеры. Итог: не один десяток малых параллельных проектов на С++ и ни одного на Хаскелле.
Вот тебе и «easy parallelism by design».
Sirikid
Программист на Фортране сможет написать программу на Фортране на любом языке. А хаскельные программы хорошо параллелятся с точки зрения компилятора из-за своей структуры. Удобный программисту интерфейс это не гарантирует, хотя сейчас такие интерфейсы есть, я считаю.
mayorovp
А оператор `par` уже устарел?
NotThatEasy
Это и есть монада
Ничего не устарело, просто я открыл для себя с изучением Хаскеля, что недостаточно просто «сказать» программе, что я хочу её раскидать на десять кластеров, а надо-таки раскидывать этот лёгкий-для-параллелизма из коробки по парам и секам.
Написать-то можно, если задаться, но к многопоточности приходишь, как набираешься чуть опыта и видишь, где от неё профит словить.
mayorovp
Нет, это не монада.
NotThatEasy
Благодарю, загуглил сигнатуру, был сбит с толку другим результатом гугла.
Tzimie
Точно также в сиквеле, надо сразу думать множествами, а не мыслить "так, берем один элемент, проверяем..."
0xd34df00d
Зачем там монады, если можно практически любой
map
заменить не думая наparMap rdeepseq
?Есть, конечно, и монада
Par
, но она для чуть более сложных задач.maxzh83
Не зная как устроена Map, это сложно понять. Не исключено, что в Map метод добавления элемента генерит новый Map просто (как в Scala).
CheY
Делаем простой и понятный язык для человека, поэтому добавляем побольше разномастных скобочек, стрелочек, знаков, двоеточий, точку с запятой в конце! *картинка с изображенеим genius*
math_coder
Да-да. На первый взгляд новый язык по читаемости может поспорить с Perl, J и триграфами C++.
Мне кажется, я даже мгновенно перестал ненавидеть JavaScript после того как увидел это. Серьёзно, ещё пол-часа назад я хотел любыми силами избежать JavaScript в моей жизни, а сейчас JavaScript уже не кажется чем-то сильно плохим.
cubit
Наверно, для этого и создан новый язык, чтобы программеры понимали, что все не так плохо))) Это, конечно, шутка.)
Balek
Как вам удобнее читать формулировки теорем — в кванторах или в словах?
CheY
Слабая и неуместная аналогия. Мне удобен код с синтаксисом, который вызывает минимальную когнитивную нагрузку — моя голова и так будет загружена при чтении/написании кода. Здесь синтаксис, похоже, впитал всё самое жуткое из мира c/c++, так ещё и сверху присыпал от себя.
Balek
Если я правильно понял ваш первый комментарий, вы затронули сразу две темы — и визуальный шум, как точки с запятой, и специальные конструкции, как стрелочки. Насчёт шума я вас полностью поддерживаю. Но я хотел узнать именно про второе. Меня удивляет, что есть люди, предпочитающие писать «function», а не "=>". Пытаюсь понять причину.
CheY
Не, стрелочка в определении лямбд у меня нареканий не вызывает — это уже устоявшийся вариант.
Я про другое. Если взглянуть на этот код, то можно увидеть следующие «интересности»:
1) в классе (который назвали entity) есть const поля, а есть field. Казалось бы и то, и другое — поля класса, но к первым в коде обращаются через явное указание типа и двоеточия (
Board::playerX
), а ко второму через точку и this (this.cells
). Нужно ли было делать такую разницу для данных в одной области видения?2) стрелочка используется и для вызова методов класса, и для пайпинга. Нет, конечно, можно подвести эти вещи под один знаменатель, если представить что каждый метод неявно принимает параметр this (вроде того, как это сделано в Питоне). Но нужно ли?
3) потом мы видим, что через двоеточия мы по сути можем обращаемся к статическим const полям (
game = game->makeAutoMove(Board::playerX, 0);
). При этом само поле не помечено как static, хотя спецификатор этот есть (factory static createInitialBoard()
). Кстати к этому статичному фабричному методу мы обращаемся уже через указание класса и собаку (field board: Board = Board@createInitialBoard();
). Зачем надо было делать такое различие — не ясно.Да, может всему этому есть объяснение и сложно судить по куску кода о языке. Но серьёзно, как отметили выше, ощущение такое, что ребята просто на ходу сочиняют разрозненные куски, а потом под это подведут кривую спецификацию. В то время как лучше было бы начать с другого конца.
Spaceoddity
Мне удобнее. Потому что function сразу бросается в глаза (особенно с подсветкой современных редакторов), заставляя заострить на этом куске кода внимание. "=>" же беглым взглядом вообще мало отличимо от оператора присваивания.
Ну и вот, например, тут достаточно о разнице между function и "=>". Помимо того факта, что для этих стрелочек обычно надо ещё какой-нибудь Babel городить.
mayorovp
Там ровно два различия: свой this и свой arguments. Но если функции нужен свой this — то это никакая не функция, а метод; а для методов в свежем стандарте есть свой синтаксис. С arguments же всё ещё проще: надо перестать их использовать.
veslemay
Скорее в древних блокнотах. Современный редактор умеет в семантическую подсветку. А подсветка уровня кейвордов — это про древний блокнот.
Соответственно — function является лишним синтаксическим шумом, т.е. редактор умеет красить функции в уникальный цвет. И современно без разницы какие они.
veslemay
>> впитал всё самое жуткое из мира c/c++
Что конкретно?
daiver19
На словах или в коде (в добавку к формулам). Очень часто тривиальные для понимания вещи прячут за вырвиглазными формулами с кучей нечитаемых переменных (речь про статьи по cs если что).
Balek
Вероятно, люди деляться на тех, кому проще в кванторы, и тех, кому проще в слова. И это очень странно. Потому что смысл абсолютно одинаковый, но короткие специализированные знаки должны считываться и обрабатываться мозгом быстрее. Возможно, что вы недостаточно выработали навык их чтения. Но я встречал других людей, кто предпочитает естественный язык специализированному.
jevius
Зачем?
Alexey2005
Это управленческий паттерн «незаменимая команда». Когда команда, разрабатывающая один из ключевых продуктов компании, переводит его на специально ими выдуманную технологию — новый язык программирования, принципиально новую СУБД с ни с чем не совместимым языком запросов, пишет с нуля новую ОС, на которой будет крутиться сервер продукта, ну и т.д.
В итоге команда, которой удалось реализовать этот паттерн, надолго становится незаменимой — их становится очень сложно уволить, не завалив разрабатываемый продукт, потому что спецов по новой технологии в ближайшее десятилетие будет исчезающе мало.
Именно поэтому программистам очень выгодно реализовывать этот паттерн. А вот для менеджера, допустившего такое, например переписывание половины браузера на принципиально новый язык — признак профнепригодности, особенно когда компания испытывает финансовые трудности.
Paskin
Это да, но самые упоротые — это «независимые консультанты». Во времена оны приходилось сталкиваться с персонажем, предлагавшим оптимизацию программ для (внимание!) «полного соответствия Ruby-way». Что самое удивительное — его даже кто-то нанимал…
balsoft
У всех остальных языков есть Фатальный© Недостаток™.
TheShock
Теперь мало того, что вместо вставления кода текстом — он вставлен картинками. Так мы теперь ещё и второй раз обливаем читателя дерьмом, используя для этого вместо моноширинного шрифта совершенно нечитабельный serif и блюрим картинку:
Давайте вы её еще под углом сделаете и мигающей, вдруг ещё остались люди, которым удобно читать это уг?
EDIT: Оказалось, что блурилось из-за масштабирования в браузере, но это всё-равно камень в огород автору статьи
TheShock
По идее если подсветить код как JavaScript — должно быть неплохо
varanio Автор
единственное место, где я нашел подсвеченный код — это pdf файл. Какой там шрифт есть, такой и есть.
Можно разве что побольше сделать картинку, чтобы читалось получше
varanio Автор
в общем, я сделал шрифт побольше, но хабр зачем-то искажает размер картинки, поэтому замыленность всё равно есть
Chaos_Optima
А если просто переписать код его вроде немного? Ду ну бред какой-то.
Cerberuser
И вручную раскрасить? Или оставить равномерно чёрным, чтобы плевались теперь на это?
Chaos_Optima
Как выше уже продемонстрировали можно включить подсветку js, а так да, текст всегда лучше чем картинка с текстом, ну если только ты не капитан джек воробей.
creker
Это явно не язык, который ставит задачу завоевать рынок или даже быть примененным в продакшене. Это исследовательский проект, чтобы найти новое направление развития языков программирования. Тут, похоже, под ясностью и для человека, и для машины понимается создание практичного для обычного человека языка, который может формальную верификацию. Мое беглое чтение примеров формального доказательства алгоритмов на специализированных языках повергло меня в ужас как-то давно, так что смысл в этом видится вполне.
serf
Вот вот. Среднему индусу в MS такое не осилисть.
creker
Не осилить такое почти никому, почему формальную верификацию на практике практически нигде не видно. А то, что видно, лишь небольшие кусочки и алгоритмы. В основном максимум это статические анализаторы кода и здоровенные сборники правил как писать на каком-нить С. Даже там, где от корректности кода зависят жизни людей.
0xd34df00d
Так себе язык для формальной верификации, имхо. Ну, такое сугубо любительское имхо любителя поидрисировать на выходных.
valis
И чего в нем такого нового? Исследовательского?
apxi
Языки все больше становятся похожи на какие то клиновидные записи или древнеегипетские иероглифы ('x'#PlayerMark, =>, <~, Board::playerO, @, |>, |< и т.д. и т.п. в самых неожиданных местах) и не понятно зачем вместо превычного слова «class» и «struct» или «record», которые уже не одно десятилетие в других языках применяются, выдумывать что то новое. Вместо function вставили method, тогда вместо if нужно было еще что нибудь придумать, а еще лучше вообще язык поменять на русский, была бы еще одна 1С, только в функциональном стиле.
DeuterideLitium6
Я так понял, что это означает, что функция виртуальная. Или нет?
ksbes
Ну в каком-то языке программирования (JS?) это может и так, но концептуально-то разница меду функцией и методом не такая. Лучше уж сделали бы как в С++ (virtual/override) или как в джаве — где всё виртуальное (зачем нужны невиртулаьные методы? — хороший вопрос для собеседования!)
vics001
Ответ: для того, чтобы всех запутать и после cast вызывался другой метод.
Адепты С++ ответят, что тем самым экономят место в виртуальной таблице вызовов.
0xd34df00d
Адепты C++ ответят, что тем самым, во-первых, легко получить классы без виртуальных методов вообще (что экономит место в лейауте класса и приводит к прочим приятным результатам на тему тривиальности типов), а, во-вторых, они облегчают работу девиртуализатору и инлайнеру (нечего девиртуализировать, если методы не виртуальные).
assembled
Ну почти все языки полны всяких «иероглифов», в заблуждение они вводят разве что новичков, со временем они запоминаются и проблем с их пониманием нет, конечно если не городить всяких заумных конструкций. Есть другая крайность — когда язык очень похож на естественный, как например SQL или COBOL, читать вербозный код не очень-то легко, а писать на таком языке вообще мрак и уныние.
Ага, как в Red, if с одной ветвью, either — с двумя.
worldmind
Хм, на первый взгляд крутая идея
Lamaster
> тогда вместо if нужно было еще что нибудь придумать
whatever(false condition)
mihmig
Тсс, тихо! А то ещё призовёте дьявола в 1С 8.4 в виде:
#РасходнаяНакладная^Conclude(СуммаСовсемБез НДС).Подобно()
KvanTTT
«Подобно» или все же «Подробно»?
extempl
А мне интересно что такое
Conclude
и почему не на русском? Внешняя команда или…?AndreySitaev
Вот эта формулировка меня очень и очень смущает. То есть… скажем, если программа использует БПСЧ… или обращается к жесткому диску / сетевому адресу… результатом будет синий экран?
Или вот это:
под стабильными, наверное, понимаются устойчивые алгоритмы? А если я напишу на Bosque алгоритм Шелла (неустойчивый) — будет исключение рантайм? Ошибка компиляции?
Поясните, пожалуйста. Взрывает мозг…
varanio Автор
Дословно в документации так:
Thus, Bosque does not have any undefined behavior such as allowing uninitialized variable reads and eliminates all under defined behavior as well including sorting stability and all associative collections (sets and maps) have a fixed and stable enumeration order.
As a result of these design choices there is always a single unique and canonical result for any Bosque program. This means that developers will never see intermittent production failures or flaky unit-tests!
Больше информации нет никакой.
Возможно, я что-то не так понял. Готов исправить статью
jaiprakash
Почитайте про «неопределённое поведение». Например в Си, порядок некоторых операций не определён в стандарте и разные компиляторы выдают разный результат в таких случаях.
Paskin
Авторам языка придется определить все возможные сочетания операций и выражений при всех возможных условиях. Флаг им в руки, так сказать…
jaiprakash
Но это же в любом случае приходится делать авторам компиляторов.
Хотя бы простые и общие правила не помешали бы, почему-то есть языки, где это сделано.
0xd34df00d
Нет. Отсутствие UB можно доказывать индуктивно и, как следствие, линейно или квадратично по числу правил вывода типов (см. Featherweight Java, например).
assembled
Вот если появится вторая реализация, появятся и UB.
mayorovp
UB может появляться и без второй реализации.
DeuterideLitium6
Непонятно, какие преимущество у этого ЯП по сравнению с другими? Тем же С++.
Вот мне нужен ЯП, смесь из ассемблера(доступ к флагам и регистрам процессора), С и С++(классы, виртуальные функции) и при этом достаточно простой. Думаю развивать идею ассемблера MASM'а(UASM), кодогенерирующие макросы .if, .while, .for, и т.п.
А по этому ЯП(назовём его Боська), в упор не вижу каких либо достоинств. Да, у меня память не очень хорошая, и очень не хочется её перегружать лишней инфой, изучая кучу очень сложных ЯП. Надо проще делать, чтобы простой как Lua и достаточно функциональный как С++.
Paskin
КО подсказывает, что в С/С++-программы можно делать ассемблерные вставки, использовать intrinsics или линковать к ним модули на ассемблере.
Bronx
Назовём его Босх.
nmrulin
Уже есть такой язык — Паскаль. И прост(не зря школьникам преподают) и функционален.
Terras
del
wtpltd
В статье почему-то не упомянуто, что это от команды MS Research. Т.е. пока просто какой-то эксперимент. Что-то попробовать и посмотреть, что из этого может получиться.
Поэтому, обсуждать можно какие-то моменты, с учетом того, что все может поменяться раз и на 180. А может и вообще сгинуть. Или какие-то фичи где-то прикрутят.
А так-то чего бы не написать в заголовке «MS представил общественности убйцу C#»
varanio Автор
И что, что от MS Research? Понятно, что не от MS Office. И в заголовке, кстати, нет ровно никакой желтизны.
Поясните свою мысль.
Ogra
Ну от MS Research или от MS Developer Division — огромная разница.
Желтизна сегодня — она именно такая. Написать абсолютную правду, но так, чтобы сформировать ложное впечатление. «Новый язык от Microsoft» — это явно про какой-то продукт, который распространяет компания Microsoft. Не эксперимент в Research, а конкретный продукт с поддержкой в Visual Studio.
varanio Автор
Я не в курсе про MS Developer Division и т.д. Поэтому и попросил пояснить мысль.
Newbilius
Так может быть вам как автору новости стоило бы всё выяснить, прежде чем торопиться писать?)
varanio Автор
Выяснить всю структуру Microsoft?
Если бы я знал о таких нюансах, я бы конечно выяснил.
Ogra
Хотя бы выяснить, что такое Microsoft Research, и какими проектами они занимаются? Тяп-ляп и в продакшен, да?
varanio Автор
Самоутверждаетесь в комментариях, да?
Yanis
В readme репозитория, на который вы даёте ссылку в начале «статьи», как раз и говорится, что язык Bosque это проект Microsoft Research и приводится ссылка на него. «Выяснить структуру Microsoft» не обязательно — вся информация не дальше первых ссылок поисковой выдачи. Это не так уж и сложно, если решили выложить некий обзор. Абзац с подведением итогов говорит о том, что вы действительно не разобрались что этот проект ни что иное как PoC.
devpony
Выглядит как первоапрельская шутка.
mapron
«Весь апрель — никому не верь!»
vlreshet
А я думал я один трижды проверил дату пока дочитал пост
KvanTTT
Взаимоисключающие параграфы? Языки программирования созданы так, чтобы быть понятными человеку. Для компьютера лучше всего понятен ассемблер.
Почему? Многие алгоритмы наоборот в рекурсивном виде выглядят просто и естественно. А нерекурсивный вариант выглядит куда более громоздко.
А как нужно помечать потенциально рекурсивные функции, т.е. такие, которые могут вызывать сами себя, но опосредованно?
Какие-нибудь современные языки являются недетерминированными? Вспоминается только C++. Записывать это в плюсы — все равно что писать, что в языке можно использовать не латинские идентификаторы.
Что это за чушь вообще. А что если используется функция генерации случайных чисел?
Также хочется задать авторам вопрос: чем их не устроил собственный же язык F#?
TheShock
MacIn
Программа на машинном языке тогда уже. Программа на мнемокодах (языки ассемблера) — тоже понятнее для человека.
Source
Тоже возник такой вопрос. А вообще лучше бы они Nemerle поддержали.
nmrulin
" чем их не устроил собственный же язык F#" Это теперь у всех такое поветрие — яростно пропихивать свою поделку, потом сами же забрасывать. Как ябочники вместо C++ зачем-то пропихивал objective-c, потом сами же его и забросили.
Witch-hunter
F# слишком сложен, он вдвое сложнее C#, а С# сам по себе непрост, у МС нет собственного простого языка (VB.NET не в счёт) а-ля Python/Go/PHP. Нужен простой, чтобы его можно было в школах преподавать.
0xd34df00d
Извиняюсь за занудство, но как вы определили, что именно вдвое, а не в полтора раза и не в пять раз?
Cerberuser
Видимо, .net имеет такую же сложность, как и функциональный стиль, соответственно, их сочетание сложность удваивает.
Witch-hunter
Так же, как я определяю, что одна книга вдвое толще другой: по количеству нового материала. Книга F# for fun and profit имеет аж 1930 страниц! F# может почти всё, что может С# плюс вдвое больше. Учитывая, что помимо самого языка нужно еще изучить функциональную парадигму программирования, привыкнуть к новому синтаксису, получается не проще, чем еще раз прочесть книгу по С# + GoF. Понятное дело, что инженеры, научные работники, а так же школьники, студенты, хипстеры и стартаперы не являются целевой аудиторий F#.
Sirikid
Знаете, не все были искалечены ООЯП в процессе начального обучения программированию, кто-то начинал с функционального или даже логического языка.
Witch-hunter
F# гибридный язык, так что рискнувшему его изучить придется «запачкать ноги».
0xd34df00d
Я как-то случайно наблюдал, как человек (причём совершенно не технарь) первым языком учил JS… В общем, было видно, что equational reasoning человеку куда ближе и милее, чем вся ОО или вообще императивная парадигма.
juray
а кто-то вообще с релейных диаграмм
assembled
Я не понял, сперва говорится об иммутабельных значениях, а потом о возможности объявления мутабельных переменных. Так что всё-таки иммутабельно, значения или переменные?
Но это те же интерфейсы и классы, просто названы по-другому.
Cerberuser
В такой формулировке это звучит как утверждение, что присвоить переменной новое значение можно, а вот никакой внутренней мутабельности уже нет — если нужно заменить значение поля в объекте, изволь менять весь объект, как-то так…
Gymmasssorla
Какая же инновационная идея появилась у Microsoft, никто до этого раньше даже и не додумывался.
untilx
M$ изобрели Erlang только TypeScript
IvanNochnoy
Пока это всего лишь эксперимент, и, возможно, этот проект ждёт та же печальная судьба, что и Code Contracts.
rpiontik
Че-то я не осознал цели этого творения. Если крупная компания выводит на общее обозрение нечто, пусть и не в форме релиза, но под своим именем, она чего-то ждет. Языки имеют специализацию. А тут я че-то ничего внятного не нашел.
Хотя… возможно, это очередная попытка от MS создать Java.
iluxa1810
А зачем MS создавать еще один Java?
Разве MS не создала свое виденье Java создав C#?
rpiontik
Именно. И проект, в общем-то оказался провальным. Можно сколько угодно обсуждать спешность шарпа, но его удел оказался корпоративные решения. А прибитость его к .net вообще не ясна, потому, что никакой крос-платформенности он не дает. В общем, как по мне, так акая-то хрень вышла. Как и VB до этого. Который MS сует куда не попадя, даже в свои браузеры совала. Сравнивать его с Java язык не поворачивается.
Ogra
Вы что-нибудь слышали про Unity?
Alexsey
Я просто оставлю это здесь
rpiontik
Я тоже, просто оставлю это тут.
Это указано на вики. И копипастю сюда не как пруф, а как текс с которым Я согласен. И имею то же мнение.
К кросплатформенности я отношу не просто исполнение кода на чем-то ином, а развитую экосистему продукта, адаптированную под платформу. MS же тянет все свое с собой. И… пфффф… Ну и повторюсь еще раз — это лишь мое мнение.
creker
С такой точки зрения java такой же vendor-lock, т.к. стандартное явление видеть программистов, которые живут энтерпразйным java стеком и всю жизнь за его пределы не вылазят, что их даже на работу не взять себе для чего-то другого.
А в общем, вы процитировали глупость, с которой согласны лишь потому, что не имеете представления о ситуации так же как ее не имеет автор этого опуса. Кроме этого вашего mono вообще-то есть .net core.
kasthack_phoenix
Оно какими-нибудь фактами подкреплено? На прошлых двух работах писал на дотнете совсем не под windows — мобильная разработка на Xamarin(Android, iOS) и тяжелый бэк на .NET Core(Linux) — есть вполне конкретные контрпримеры к утверждению, что оно прибито к MS-экосистеме / кроссплатформенность недостаточного качества для энтерпрайза.
nuget? Если взять топ-100 пакетов оттуда по популярности(не стал проверять дальше), все они работают на *nix без проблем.
rpiontik
Мне?ние — понятие о чём-либо, убеждение, суждение, заключение, вывод, точка зрения или заявление на тему, в которой невозможно достичь полной объективности, основанное на интерпретации фактов и эмоционального отношения к ним.
Подкреплено личным опытом, в период с момента его первого релиза до 2014г.
Alexsey
Слушайте, ну вы вроде как тим лид, да еще и с таким большим опытом работы с .net. Кому как не вам понимать что за 5 лет в современной индустрии меняется вообще все и ваш опыт .net пятилетней давности имеет мало общего с тем что мы имеем в 2019 году.
rpiontik
Или… или как тимлид с некоторым опытом, который наблюдал многострадания MS подвинуть Java, а еще и Delphi (че к нему то привязались, вообще не ясно) одним махом. При этом, завезя все, что было в VB и прибив гвоздями к своему любимому OLE. Наблюдая конвульсии шарпа с первой презентации (с криками — долой самодержавие Java!) до отказа от всех внедрений на нем в крупных компаниях и переход на Oracle.
Я таки (за 10 лет) сложил свое мнение и об языке и о его перспективах и сделал свой выбор, в свое время. И ни разу о нем не пожалел.
Вот как завезут .net на хотя бы 30% устройств как предустановленную машину, так и поговорим. А то в свое время визгу было, что мир теперь изменится, про java забудут и везде будет .net. А я как отъехал от MS так за все время, даже случайно не встретил .net на чем-то отличном от винды.
mayorovp
До наступления эпохи смартфонов .net как раз и был предустановлен более чем на 30% устройств.
А на смартфонах предустановленный .net и не нужен: mono прекрасно распространяется вместе с приложением.
rpiontik
До наступление эпохи смартфонов была эпоха динозавров, когда ничто кроме MS не было. Даже на IE 6.0 VB работал как скриптовый язык. И MS пыталась «подскочить» в уезжающий без нее поезд компактных устройств. Напомню, что MS запилила WinCE и WindowsMobile где .net устанавливалась. Ну и где оно? Это невмеру прогрессивное архитектурное и инженерное решение?
Ровно также, как только появилась Java, тут же скукожилось и съехало в небытие. Причем скукоживаться начало уже тогда, когда простые мидлеты появились на простых телефонах. Заметьте не .net, а Java машина туда ставилась. Почему бы?
С выходом первого андройда WindowsMobile был приговорен. Вялые попытки что-то там пропихнуть через покупку Nokia увенчались тоже полным крахом. Сейчас MS приторговывает… Nokia 3110 (или какого они там мамонта возродили) в оригинальном исполнении. Смешно? По мне так очень.
Возможно. Только приложения такие никому не нужны. В лес со своими дровами…
IvanNochnoy
Реальная ситуация здесь:
Developer Survey Results 2019
rpiontik
Занимательная ссылка (y)
TheKnight
Не вброса ради а любопытства для: в качестве ОС для разработчика на этом проекте вы использовали Windows или таки Linux?
kasthack_phoenix
Windows — до VS кроссплатформенным IDE не дотянуться ещё.
IIRC, ни в Rider, ни в VSCode, ни даже в Monodevelop a.k.a Xamarin Studio a.k.a VS for Mac нет edit-and-continue до сих пор.
KvanTTT
В Rider уже есть, скоро будет в стабильном релизе: www.jetbrains.com/rider/eap
LMSn
Какая-то аргументированная критика C# в сравнении с Java у вас есть?
С вашей стороны весьма глупо называть провальным язык, который уже давно занял неплохую долю в своей нише и очень крупное сообщество.
TheShock
varanio Автор
А в чем отличие, можете в двух словах рассказать?
TheShock
В Java невозможно так сделать — информация о дженериках теряется на этапе компиляции и этот код скомпилируется во что-то такое:
Естественно, работать не будет. В Шарпах информация о типах сохраняется и её можно использовать не только как подсказки, но и в коде.
mayorovp
Вот это как раз без особых проблем работает, просто вместо угловых скобок надо использовать class:
TheShock
Да я понимаю, что можно без этого. В Go вон вообще без дженериков обходятся как-то. Просто это менее удобно.
mayorovp
Тут всего-то на 4 символа больше писать надо, это совсем не то же самое что и «без дженериков». Если сравнивать с тем как в Java «реализованы» свойства — то эти 4 символа и вовсе незаметны.
TheShock
Мне оно не шибко нравится не по количеству символов, а по сути.
Да и это не единственный пример, где то, что дженерики пропадают на этапе компиляции — мешает
mayorovp
В том, что они не пропадают во время исполнения. Отсюда два следствия:
1. статические поля и методы также могут использовать типы-параметры;
2. можно без хитрых извращений и без тонн автогенерированного кода взять и использовать контейнер, параметризованный примитивным типом.
TheShock
2. Вы о том, что в Java список может принимать только Boxed Integer, но не примитивный int?
mayorovp
Ага, именно об этом. Соответственно, или мириться с постоянными упаковками, которые никакой Escape analysis не уберёт по построению — или использовать отдельный интерфейс и отдельную реализацию для списка чисел.
rpiontik
Вы мне еще мнение запретите высказывать :))) Смешно.
impwx
Похоже на тестовый полигон, на котором будут обкатывать будущие фичи для других языков.
Livid
Чего только люди ни придумают лишь бы не делать алгебраические типы…
Вообще конечно выглядит пока сомнительно. Особенно комбинация заявленной иммутабельности с
ref
— то ли авторы сами толком не определились, чего они хотят от языка, то ли не очень понимают, что immutability это совсем не то, что делает const в javascript. Вообще изрядно отдаёт какой-то фиксацией на javascript-е. Как будто авторы даже с наработками "родного" майкрософтовского F# не вполне знакомы, хотя пайп|>
явно оттуда потянули.mihmig
Хм, а мне понравился отказ от циклов в пользу перебирающих методов. Ведь при работе с коллекциями обычно требуется их отфильтровать либо агрегировать. Школьные задачки типа «посчитать количество чётных чисел в массиве, стоящих на нечётных местах» в расчёт не берём — задача синтетическая и рассчитана как раз на понимание циклов.
mayorovp
А у меня довольно часто встречаются задачи, в которых простейший цикл выглядит проще чем комбинация фильтров. Как правило, это задачи вида map + filter + map, где из первого map нужно «прокинуть» в последний исходный объект, который при этом совершенно не нужен в фильтре. Особенно плохо всё становится, когда в ходе изменения требований необходимость этого объекта в конце цепочки то появляется, то исчезает.
Cerberuser
К слову, один из таких случаев — когда последний map должен знать номера объектов в исходном (неотфильтрованном) списке.
lgorSL
Ну вообще говоря можно сначала отобразить объект в пару (объект, индекс) и потом делать всё остальное. В идеале компилятор может всю цепочку вызовов превратить в обычный цикл, но на практике, кмк, такого ещё нет.
Cerberuser
Так об этом и речь: object -> {map} -> (object, index) -> {filter} -> (object, index) -> {map} -> object_processed_with_index.
Flammar
Тогда начать можно со списка индексов, хотя это выглядит не вполне красиво. Я так делал с GUAVA c использованием
Lists.transform(new AbstractList(){...}, ...).
Flammar
Ну я тоже так делаю, но мне это напоминает «строительство клозуры (=замыкания) вручную». Не особо элегантно.
Source
Меня несколько смущает, что ничего не сказано про ленивость вычислений. Если уж отказываться от циклов, то это must have.
Escalibur
Я тут написал статью, где были подвергнуты критике две любимые концепции нынешнего ИТ — очередь и стек.
Так же была описана концепция OS BlockOut (разработанная в 1995), где все процессы организованы в сети процессов, обменивающихся сообщениями. Входами сетей являются внешние события, выходами — выходные устройства. Сети передают сообщения без буферизации, то есть размер очередит всегда == 1, соответственно, никогда нет рекурсий, то есть стек не нужен.
Статья была перенесена в черновики, так как столкнулся с «хабраэффектом», а именно, травле по принципу «белая ворона» в стае чёрных. Попытки аргументированных споров были, но минимум. Большая часть аргументов была в стиле: «ты — дебил». Соответственно, всё это сопровождалось минусованием и невозможностью нормальной дискуссии по правилам размещения сообщений.
Что мы видим в данном языке: та же попытка сетей процессов, обменивающихся сообщениями которые идут по каналам. То есть моя концепция 1995-го года.
Далее, есть ещё одна тема, которая вызовет у любителей современного состояния ИТ страшное раздражение. На эту тему я ещё статей не писал. Это необходимость исключения высокоуровнего императивного программирования и тотальный переход на декларативное. Только эта парадигма позволит нормализовать ситуацию с качеством ПО, так как даст возможность создавать программы, которые позволяют проверку на корректность и смогут избегать дурацких ошибок императивного стиля. Так же, несомненно, это позволит как автоматизировать производство ПО, так и повысить продуктивность разработки.
Дак вот, этот язык — попытка добавить декларативности в программирование. Эти самые пайплайны — это и есть декларативность низкого уровня, то есть с довольно простыми алгоритмами в виде замены циклов. Ты описываешь структуру обработки, а система программирования сама подбирает как выполнять обработку, то есть оптимизировать циклы. Не радикальное улучшение, но уже что-то.
Таким образом, данный язык — несомненно шаг вперёд, но, скорее всего, он далее лабораторий МС не пойдёт, как многие хорошие вещи, которые там разрабатывались, к примеру Singularity.
Escalibur
Два минуса и как обычно без объяснений )) Люблю хабр! Товарищи, обосновать не хотите? Так-то это хамство. И то, что вам это позволяют авторы сайта, не означает, что вы так себя можете вести!
mayorovp
Не вижу в этом языке никаких процессов и каналов. Pipe operator только называется так, а дальше коллекций дело не уходит.
За процессами и каналами — в go, в Erlang, или в Java и её библиотеку Akka, или в .NET и в библиотеку Akka.NET. Но там очереди, почему-то, есть. Как и стек. Не согласна почему-то реальность с вашими фантазиями…
Escalibur
Коллекции + map reduce хочу заметить. А этими парадигмами закрывается львиная доля задач.
Ну и насчёт реальности, вопрос времени. Когда кончатся индусы с китайцами, придётся что-то делать, чтобы создавать софт. И это точно будет не императивная парадигма.
mayorovp
Львиная-то львиная — вот только совсем не та, для которой обычно очереди используют.
Escalibur
У меня один простой вопрос к любителям очередей: а зачем?
Опять же практически процитирую сам себя:
Очередь — это буфер. То есть мы пихаем в буфер некие элементы в надежде на что? Я так понимаю на то, что у нас сейчас заняты некие ресурсы и мы надеемся, что они в какой-то момент освободятся и мы сумеем обработать то, что набилось в очередь.
Тут есть два варианта. Во-первых, зачем так проектировать систему, что она не справляется с нагрузкой и приходится ждать в очереди? Во-вторых, а если мы не дождемся освобождения ресурсов, либо их таки не хватит на разгрузку очереди, то что делать?
На первый вопрос ответ понятен: просто не умеем нормально проектировать. В результате, к примеру, лунные модули не садятся на Луну. Но это то, что видно, а бОльшая часть косяков, связанных с неправильной оценкой производительности, не видна, так как системы не настолько критические. Единственно, что я могу признать как вариант нормального использования очереди — это выравнивание пиков нагрузки. Но тогда очереди должны быть хотя бы ограничены по ёмкости. То есть мы должны осознавать какую нагрузку мы должны принять и выравнять.
Про второй вариант ответ так же прост: мы просто сбросим содержимое очереди. Дак зачем копить очередь, занимая ресурсы, если можно сбрасывать элементы сразу? Зачем ждать, пока клавиатура запищит от переполнения буфера, если можно просто до него не доводить, а блокировать приём новых элементов?
Я, конечно, не Эйнштейн, но метод Эйнштейна весьма полезен. Нужно иногда брать аксиому и превращать её в теорему. То есть пытаться понять, а аксиома ли то, что мы считаем аксиомой. Дак вот, рассуждения об очередях и стеке — это типичный пример того, как если порассуждать об аксиомах, окажется, что они вовсе и не аксиомы.
mayorovp
Потому что пиковая нагрузка обычно выше чем средняя.
akryukov
Уважаемый, вы путаетесь в показаниях.
Вот за этим:
С одним дополнением — вы не всегда можете так просто ограничивать очередь по емкости. У вас может быть требование от бизнеса "обработать все входящие запросы".
Вы опять отвечаете на свой вопрос самостоятельно:
Такие ситуации нужно отслеживать и предотвращать. Ждать когда мощностей даже с очередями не хватит — глупо. Нужно с самого начала собирать статистику нагрузки, экстраполировать тренд и масштабироваться с учетом будущей нагрузки. Если ресурсов внезапно не хватит, то это ЧП и отказ в обслуживании части клиентов.
Если вы где-то встретили решение, которое использовало очереди не по назначению, то это проблема не "очереди", а решения.
Что вы будете делать, если у вас требование от бизнеса "обработать абсолютно все входящие сообщения"? "Просто сбросить" далеко не всегда так просто, как хотелось бы. Это вы видите запросы в очереди, а другие видят в них деньги и время.
Озвучьте, пожалуйста, аксиому, которую вы пытаетесь превратить в теорему?
Не помню, чтобы кто-то всерьез заявлял "нужно везде использовать очереди".
evocatus
Зачем? Функциональный, типизированный, иммутабельный, но с возможностью обойти.
Есть же F#, Clojure и Haskell (первый и третий при поддержке Microsoft)
jbolshakov
Согласен с вопросом. Язык похож на F#. Там так же можно сделать многое из статьи и mutable переменные есть.
alloky
Почему в новых мода описывать аргументы функций как имя: тип? Чем их не устраивает обычный стиль, тип имя?
Cerberuser
Как минимум одно из обоснований — чтобы спокойно прикрутить вывод типов и не заставлять писать везде `auto` или его аналог.
veslemay
Ни от какого auto паскаль-стиль не уходит, а наоборот требует ввод кейворда для обозначения декларации переменной. Таким образом вы всегда будите писать синтаксический мусор вместо типа. Всякие там var, let и прочее.
С типом будет ещё больше мусора, а именно let/var name: type, а не type name;
math_coder
Go опровергает ваши утверждения.
veslemay
Что именно он опровергает? Поподробнее. Вводом := или подобной фигни? Ну дак это не опровержение — это подтверждение, т.к. это и есть кейворд для отделения декларации от использования. К тому же, он к делу отношения не имеет, т.к. почти во всех языках используется var/let. И говорим мы о таком языке.
creker
Вот именно что опровергает. := позволяет опустить var и начать объявление с идентификатора переменной. Синтаксис полностью однозначен — либо сначала идет ключевое слово, либо идентификатор. Если мы ставим первым тип, то начинаются вечные проблемы с поиском этого типа и определением вообще, что это за хрень, чтобы потом уже определять, то ли это возвращаемое значение функции, то ли это переменная или еще какая хрень.
veslemay
Я уже объяснял — это не работает. Причин тому масса. Основная из них заключается в том, что как звучал тезис? «имя: тип что-то там позволяет». := — опровергает этот тезис, т.к. в данном случае абсолютно неважно где и как написан тип — одного «имя: тип»/«имя» недостаточно для определения переменной.
К тому же, := — является таким же кейвордом, о котором я говорил. И абсолютно неважно в каком он будет виде. Т.е. мой тезис это никак не опровергает.
К тому же, это вообще находится за рамками контекста т.к. контекстом является а) синтаксис принятый в обсуждаемом языке, б) синтаксис принятый в си.
Так же, := — избыточен для ситуаций, где тип не выводится, а задаётся. Вам придётся либо использовать избыточность, либо вводить два типа операторов, что опять же — избыточность и синтаксический мусор.
К тому же, это локальные го-заморочки не присущи ни паскалю, ни всему тому, что используется его стиль.
А так же, самое важное — это вообще никак не зависит от стиля. Это работает в любом случае. Т.е. смысла приводить это в контексте разделения стилей нет — т.к. это не относится к одному из — это относится к обоим.
MacIn
Если по сути, а не формально, то тезис звучал как «имя сначала, тип потом Vs тип сначала, имя потом». Далее спор шел о ключевом слове auto для авто-вывода против якобы необходимого var для обычного объявления. Двоеточие это только пример реализации первого подхода. Пример го показывает, как можно иметь имя сначала, не имея «синтаксического мусора» перед именем.
У вас простое взаимное недопонимание.
veslemay
Нельзя. := — это синтаксический мусор, который, к тому же, писать сложнее нежели =, либо auto.
К тому же, как я уже говорил — мы либо вводим :=/=, либо мы имеем не только := в качестве синтаксического мусора, но и аннотацию при которой := избыточно.
Это так же неверно, т.к. двоеточие не имеет никакого отношения к подходу.
Я больше скажу — в си вообще аннотация типа ненужна. Она используется только для в одном случае — определение. Да и то нужен просто какой-то флаг для выражения намерения. Подойдёт вообще что угодно. const/auto/register. Всё это уже придумано и давно. Го просто взял эту логику из си.
creker
Во-первых, не мусор, а однозначность синтаксиса как для парсера, так и для человека. Во-вторых, писать это несравнимо проще auto. В-третьих, таки ничего лишнего перед именем переменной нет.
Это ли не тот синтаксический мусор. Вместо типа мы пишем вещи, которые вообще не имеют никакого отношения к делу. Модификаторы доступа и устаревшие ключевые слова, которые нынче ничего не значат.
В этом нет логики, это синтаксические извращения. В Го однозначный синтаксис, которые имеет четкие правила — либо :=, либо var. Все. Пример тут не с кого брать было.
veslemay
Мусор. Вы как бот повторяете один и тот же набор шаблонных фраз.
Основания этим заявлениям.
Есть после имени. До оно, либо после — это ничего не значит.
Нет, вы действительно бот. Какое отношение эти рассуждения имеют к теме? Никакого. Абсолютно неважно что они значит — они значат тоже самое, что и :=, только ещё больше.
Чушь какая. Они имеют отношения к делу и никто их вместо не пишет. Вам лишь сообщили, что подобная логика объявления без типа существовала и существует в си.
Вы опять где-то что-то услышали и начали повторять? Ну расскажите мне про то какой бесполезный статик, либо const, либо volatile. Или это, как обычно, пустой трёп?
Опять же, когда нечего сказать — пиши херню.
Мне надоело с ботом разговаривать.
Хотите поиграть — хорошо, я жду эквивалента кода:
int a;
На го.
math_coder
Это не код, это говно (неинициализированная переменная).
А что касается того, что опровергает Go, то Go опровеграет два ваших утверждения: 1) необходимость мусора в виде двоеточия, отделяющего имя переменной от типа, 2) необходимость мусора в виде ключевого слова типа
let
,var
илиauto
.Двоеточие в
:=
не является отдельным элементом/ключевым словом. Два символа:=
являются одним, единым оператором, поэтому никакого мусора при его использовании уже нет. (Не будете же вы настаивать, чтобы присвоение значения переменной выполнялось вообще без единого оператора?)veslemay
Т.е. ответа нет и адепт го порвался?
Фантазии брызжущих слюной адептов го.
Я уже отвечал на эту ахинею. Эта ахинея никак не связана с темой и с моим утверждением, т.к. она применима не только к паскаль-стилю.
Т.е. я могу использовать := в любом из стилей. Это просто сахар, костыль. Определяются переменные го не так.
Что за нелепая попытка врать. Читаем мой коммент:
Т.е. брызжущий слюной адепт го врёт, утверждая, что говорил «необходимость мусора в виде ключевого слова типа let, var или auto.», но я приводил это лишь как пример.
Является.
Два символа в if «являются одним, единым оператором». Опять методичка потекла.
Он и есть мусор, т.к. есть уже оператор =. А это другой оператор, который является костылём для решения избыточности паскаль-стиля.
Опять чушь. Именно те, кто рассказывал про «проблему auto» утверждали, что переменную можно объявлять без избыточности.
Я же сообщил, что это неверно и нужен механизм отделения. В ситуации же си-синтаксиса механизм ненужен — он уже есть.
В ситуации же с паскаль-синтаксисом он нужен. Это var/let и прочая фигня. Именно с var сделан ваш го. А далее закостылено := — которая является таким же механизмом, лишней сущностью она повторяет уже существующие функционал.
Вообщем, вы, насколько я погляжу, особо вменяемостью не блещете. Спорить о чём-то с ботами бесполезно, поэтому я вернуть к сити, а именно.
Я отвечал изначально в контексте преимущества паскаль-стиля, т.е. я это опровергал. Теперь вам нужно сообщить — каким образом := относится к паскаль-стилю? Я заранее отвечу — никаким. Новы там можете попытаться что-то придумать.
veslemay
Я даже не поверить, что я отвечаю на настолько нелепые рассуждения.
Есть синтаксис var name type =, базовый. Данный пациент утверждает, что никакой избыточности нет. Но авторы го с ним несогласны.
Именно поэтому вводится var (), это есть ещё со времён паскаля. Потому что все очевидно, что объявление с аннотацией через var — это избыточно. Т.е. пациент спорит с объективной реальностью.
Аналогично с := — это синтаксический сахар, который был добавлен специально для решения проблемы var name auto =.
Т.е. мы имеем как минимум два варианта объявления переменных, но что же там нам сообщал пациент:
Он заявлял, что проблема С++ в том, что синтаксис разный. В целом эти базовая методичка го «делаем что-то одним способом», но.
Как мы видим, что тут эта методичка ломается. Потому что способов как минимум 2(3). А почему? Потому что общий случай избыточен и добавлен специальный сахар для решения проблемы.
evocatus
Обычный это как в Algol 60?
akryukov
Активно пишу на scala, где аргументы описываются как имя: тип.
На мой взгляд, такой стиль побуждает думать сначала о смысле переменной, а потом уже выбирать подходящий тип.
Допустим пишу функцию "calculate". Например для вычисления баланса по множеству операций. Сначала есть код:
function calculate(
Эта функция принимает в себя множество операций. Добавляю
function calculate(operations:
Какую лучше коллекцию сюда передавать? Сортированный список или set. Продумываю оба варианта и выбираю список. Тогда добавляю в код
function calculate(operations:SortedArray){
Мелочь, но позволяет думать в первую очередь о бизнес-логике, а потом уже о инструментах для ее реализации.
veslemay
Тип и определяет смысл.
Тут ваша логика дала течь. Множество уже тип, вы подумали о типе до.
Опять же, существует два варианта — от данных, либо от операций. От данных — это когда у вас уже есть логика, которая генерирует данные и типы их уже выбраны. Тогда вы в первую очередь должны писать тип.
От операции. Ты вы пытаетесь реализовать какую-то операцию и в процесса выводите тип. Очевидно, что этот процесс находится в теле функции и с какой он находится — неважно. Абсолютно.
Flammar
Чтобы иметь конструкцию [имя][[: тип][ = значение]].
vitvakatu
Странно использовать слово "обычный" в этом контексте. Парсерт непротиворечимой грамматики написать легче в этом случае, только и всего.
surly
Ага, а потом начинается «прелесть»: *(** тип[ ]) имя()[ ]
veslemay
Эта прелесть никак не связана с темой. Она возникает потому, что в этой языке возможностей куда больше. Нет нет альтернативы, которая бы могла всё тоже, но как-то иначе. Выразительные возможности паскаль-стиля крайне скудные.
В си-стиле выражается void(a, b, c); В паскаль стиле нет. В том же TS приходится эмулировать сишный стиль через (a, b, c) => void, т.е. семантика потекла. Нужно два синтаксис для определения типа и функции.
И таких примеров масса. К тому же не стоит сравнивать скриптуху, где одни ссылочные типы и си. Это разные весовые категории. Это как сравнивать С++-лямбду и js-лямбду.
жс-лямбда мало того, что дырявая(вы не сможете там определить тип возврата), дак ещё и десятой долей функционала не обладает. Конечно, манипулировать и сравнивать несравнимое удобно. Но не стоит так делать.
nmrulin
Так как раз имя: тип очень логичный стиль применяется ещё в Паскале. А С++ стиль тип имя замедляет чтение кода. Обосенно плохо когда описывается сложный тип например
struct building //Создаем структуру!
{ char *owner; //здесь будет храниться имя владельца
char *city;
…
Мы сначала говорим, что это структура, потом даём имя типа, а потом уточняет.
А логично делать наоборот
building =record и дальше уточнение.
Вообще с порядком в С++ но очень.
Например логично закодировать. Указатель на хрю-хрю как ^hru_hru. Или Хрю-хрю указывает на — hru_hru^. А если сделать обратный порядок, получиться чтение справо налево.
veslemay
Почему? К тому же везде и всюду сделано аналогично и здесь:
function update
entity Foo {
И так далее.
Распарсить это решительно невозможно. Это какой-то набор слов. Напишите что-то более адекватное типа «в крестах так: бла-бла, а нужно так: бла-бла».
Bronx
Это как раз ближе к
var foo
, потому что слева от имени стоит не тип, а ключевое слово, категория имени (функция, структура или переменная), а тип как раз стоит справа: параметры и возвращаемое значение для функции, список членов для структуры и имя типа для переменной:veslemay
Это какие-то попытки подменить изначальный тезис. Был пример со sturct — я показал, что в этом языке используется такая же форма определения объектов. Она используется везде, в том же тайпскрипте.
Где тут: struct name — стоит слева тип? Нигде.
Начнём с того, что сишный sturct под неё подходит, а говорилось именно о нём. Так же никакое положение в таблице не обосновывается, никак. Т.е. на каком основании "(id: int, data: string)" является «domain», а bool не является. К тому же, ваша табличка игнорирует override method, ref env.
Ну и на самый главный вопрос нет ответа. Почему? Можно нарисовать какую угодно табличку, но вопрос заключается в том — почему так должно быть? Зачем строить свои рассуждения на догматах?
Bronx
Ок, прошу прощения, проглядел пример
building = record
от nmrulin и почему-то решил, что вы всё ещё про запись "тип имя", так что действительно вышло не в тему.(id, data)
— это "вход" функции, область определений (domain), аbool
— выход, область значений (codomain). Записьf(int, string) => bool
илиf(int, string): bool
вполне математична: "f
есть отображение множества кортежей типа(int, string)
на значение во множествеbool
".А как прочесть запись
bool f(int, string)
в парадигме "тип имя"? "Функция типаbool
с именемf
"? Но беда: тип этой функции неbool
, а вовсе дажеbool (int, string)
.veslemay
У вас дара в логике.: — семантически отделяет тип. Вы игнорируете сей факт и свободно подменяете семантику: во имя подбития реальности под свою модель.
К тому же, вы взяли какую-то логику и каким-то образом определили её за правильную, а далее исходите из того, что всё что иное — неправильное. Ведь любая другая логика — неправильная.
С таким же успехом можно сказать про ref и про override и прочее. Вы говорите о каком-то примитивном обрубке, а не о каких-то общих правилах.
В общих правилах определяющим является имя, а уже далее что-то там из него следует. Но если имя является определяющим, то на каком основании оно должно быть вначале? Да ни по каким — это догмат.
Начнём с того, что сишный синтаксис создан как f(a, b) {} — изначально все типы были auto(из крестов). Правда там не было вывода типов, а универсальный тип просто был интом.
Всё что слева/справа ничего не значит. При этом в си есть прямое разделение на право и лево. Есть тип, а есть объект. То, что написано справа — относится к объекту, а всё что слева — к его типу.
В примитивных языка такого разделения нет — на то они и примитивные. В том же bosque разделение уже появляется — там есть тот же ref, т.е. то, что относится к самому объекту.
Можно, конечно, делать это делать слева до :, как многие и делают. Но, очевидно, что получается полный треш. Всякие там name?: type.
Правда в Си всё не совсем так. Там тип не может быть массивом, потому что массив — это объект. Так же как типом не может быть функция.
Хотя, конечно, в си есть и тип-функция и тип-массив, но только в рамках тайпдефа. Так же, есть (int[]){} — это логика исходит из того же. По такому же принципу работает using в С++.
Если проще. Объект может переменной — это просто тип. Если мы видим просто тип — это тип какого-то объекта/переменной.
Но, в случае с [] — типом будет массив. Объект будет массивом. А типом указывается тип возврата операции/тора [], аналогично и с () — скобочки уже из объекта делают функцию. А всё остальное к делу отношения не имеет. Тип, который слева — это не тип объекта — это тип возвращаемый (). Потому что свободный тип — это всегда объект(какого-либо значение).
Поэтому си читается следующим образом. void f(); f — это идентификатор/имя. Вы без проблем можете писать void f()[] — функция будет возвращать массив. Тоже самое с [][], вы можете написать int arr[10]() — тоже без проблем.
И это бы всё работало круто, но в си есть фундаментальное разделение. Есть объекты как значения(которые могут быть значениями), а есть которые не могут. Вот [] и () значениями быть не могут.
Таким образом сишный синтаксис и его семантика куда строже и логичнее, нежели упомянутая вами. Где вы как угодно трактуете каждую закорючку.
Описать его можно так. Есть база, которой вы даёте тип. Тип задаётся как справа так и слева. Причём есть два типа типов. Тип самого объекта(массив/переменная(значение)/функция), который пишется справа. И всякие расширения — это слева.
f() — это функция, тип объекта. Слева накидываются всякие свойства, свйоства накидываются ИМЕННО на объект(это упомянутая мною база). f тут уже вообще не при делах.
() — расширяется параметрами. Так же, он возвращает какой-то объект. Тип объекта справа не написан — значит это обычный объект/значение. Этот объект является базой. Ему можно задать тип и всякие там квалификаторы.
* — это тип. Никаких «на» там нет. Это просто упрощение. * — такая же база. Правила те же, но она слева и приоритет у неё меньше. Именно для этого там скобочки — они выделяют новую базу.
И теперь, я думаю, вы сами сможете прочитать bool f(int, string)
База по умолчанию — идентификатор. Идентификатор имеет тип-объект функция. f — это функция, () — имеет семантику «аргументы и „возвращаемое для операции () значение“(по-колхозному возвращает значение)». Читаем аргументы. Аргументы типа инт и строка. Далее эта функция что делает? Возвращает значение(справа ничего нет). Далее, слева могут стоять уточняющие, а именно тип значения и его квалификаторы.
Так же там могут стоять «квалификаторы», которые воздействуют на сам базовый объект.
f — функция с аргументами инт и строка, возвращающая(по-колхозному) значения типа bool.
Bronx
Это скорее у вас дыра в понимании. Семантика двоеточия зависит от контекста: для переменных отделяет тип, для функций — тип возврата. Полный тип может включать в себя больше чем то, что после двоеточия, но правило то же:
В результате первого правила функции получают настоящий тип, а не несовместимый "тип-объект с расширениями", становятся гражданами первого сорта, могут быть присвоены переменной, проверяются при присваивании как по параметрам, так и по возвращаемому значению, иначе будет ошибка типа.
И, заодно, массивы теперь получают полноценный тип, как и любой другой агрегат или строка (массив символов), а не "тип-объект", чей тип при передаче в функцию внезапно протухает и разлагается до указателя.
Но очевидно, что
name?: type
— это просто сахар для каноничной записиname: type | undefined
, который слишком часто встречается и слишком длинно писать каждый раз. Правило 1 выполняется:?:type
находится справа отname
.Параметрами тоже слева? Упс, строгая логика поломалась — параметрами расширяем таки справа, т.е. внутри
()
. И теперь у нас расширения то слева, то справа.Вы уверены? Я плюсовик, не голый сишник, но насколько мне известно, ANSI C не позволяет функциям возвращать массив, только указатель, и объявление будет примерно таким монстриком:
void (*f())[N]
. В высшей степени интуитивная конструкция.Претензию про ref и override так и не понял: что конкретно вам не нравится, и как именно они ломают логику?
veslemay
С чего вдруг она меняется? Основание.
Т.е. двоеточие не имеет никакого отношения к типу. Из этого прямо следует, что тип не только справа от двоеточия, либо только. Третьего не дано. Либо двоеточие ничего не значит.
Что самое интересное — эти манёвры следствие попытки оправдать неоднозначность, которой там, на самом деле, нет. Т.к.: там там же отделяет тип. И типом является именно возврат, ведь тип возврата и определяется. Аналогично работает и для ?:type. Когда? является признаком опциональности и точно так же относится к имени, а не определяет ?:type — новый синтаксис для типа. Потому что тогда нужно было ?type, либо type?..
Вам не нужно мне перечислять те случаи, где его семантика потекла — я и так это знаю. Нужно отвечать на вопрос а) зачем оно нужно. б) почему у него изменяется семантика.
Всё это хорошо, но без интеграции в предыдущие рассуждения — это просто перечисление не относящиеся к теме. Интегрируйте его в изначальную вашу модель. public — это категория? Что это?
К тому же, вы так и не обосновали разделение в таблице. На каком основании часть после двоеточия вынесена, а список параметров остался.
Какие критерии для «настоящий»? Почему меня должен волновать какой-то фентезийный «настоящий тип».
У вас опять рассуждения потекли. Вы там забыли про ref/const и прочее. Это такие же «расширители» и без ник никакая аннотация типа не определяет объект, а является лишь частью определения.
Ну во-первых это наивные манипуляции, когда сравнивается несравнимое. Т.е. берётся скриптуха и её примитивная модель и сравнивается с более сложной моделью, при этом примитивность и её следствия превращаются в следствия синтаксиса/«настоящих типов».
Опять же. Сравнение примитивной скриптухи и более сложной модели.
Я не понимаю две вещи. На каком основании вы дёргаете куски фраз и отвечаете только на удобное. Зачем вы мне повторяете то, что я уже до вас вам сообщил?
Здесь именно говорится о том, что ?: — справа от name. И именно поэтому этот пример называется трешем.
При этом это я говорил про этот: [Int, ?:Bool], никакого name тут нет. А вот лево есть. Тут, конечно, можно пытаться задним числом опять изменять семантику для :, но очевидно — что это эквивалент name?:type и значит это не type|undefined, а именно опциональный аргумент. Мне лень читать и искать это в мануале, но любо это дыра в логике языка, либо действительно так.
С чего вдруг? Опять нелепая попытка дёргать фразы. Расширения — это квалификаторы, в разных контекста это понятия значит разное.
То, что вы увидели где-то два похожих слова и вам показалось, что вы что-то там нашли — это лишь вам показалось.
С чего вдруг она справа, если они внутри? Отсчёт идут от () — очевидно, что внутри никаким образом не может быть ни справа ни слева.
Я уверен. К тому же у вас опять какие-то проблемы с восприятием. Мы говорим о синтаксисе, а не о том, что си позволяет/не позволяет делать. Синтаксис это позволяет.
Основания в студию.
Да, куда более интуитивная, чем рандомный набор символов и дыр, который существует в скриптуха-паскаль-языках.
Конечно, куда там этим конструкциями до *[N]void f(); — вот оно, чудо человеческой мысли.
Или это: []int{1000, 2000, 12334} как перепаста с сишного (int[]){1000, 2000, 12334}. struct{f int}{50} — вот оно, удобство.
Я уже обозначил выше проблему — как это интегрируется в вашу табличку.
nmrulin
Ну так вы справо налево читаете все предложения видимо, а теперь прочитайте как все обычные люди слева направо. И расшифруйте выражение "^hru_hru" — так, чтобы каждому обозначению соответствовало слово. А теперь попробуйте сделать это c выражением hru_hru*. У вас это не получиться.
В этом то вся и проблема. Некоторые последователи С++ именно, что могут только «парсить» текст. А забыли, что 99% текст не парсят, а читают. Так вот напоминаю вам что чтением в отличие от «парсения» является процесс восприятия информации слева направо. Поэтому можно написать только лишь предложение «Указатель на переменную хрю-хрю». И это будет для всех текст, только для вас набор слов. И наоброт, предложение «Хрю-хрю на укаказатель переменную » -будет для вас текстом, а для 99% людей набором слов.
veslemay
Дак вы должны это сделать — как я буду читать непонятно какие, придуманные вами, символы?
Как нам разница — как и кто читает? К тому же нужно отличать чтение от понимания. Читать можно как угодно. К тому же, прочитайте мне 123.123. Прочитай мне a + b * (c + d).
Я могу читать как угодно, как и любой другой человек. И все именно так и читают. Читают таблицы сверху вниз, либо таблицы нужно запретить? Ищут ключевые слова в текста. Запоминают контекст.
Указатель — это тип *, T — это тип возврата операции разыменования. Слева всегда стоит конечный тип. Сишные определения читаются как математические выражения. Вот паста, где я разбирал «как читать».
Bronx
Потому что если мы принимает порядок "тип имя" для аргументов функций, то для единообразия придётся применять этот порядок везде: при объявлении переменных, членов структур, возвращаемого типа у функции и т.п. И получаются приколы с неоднозначностью типа most vexing parse. Ну вот, например:
Foo foo();
— это объявление и инициализация переменной foo типа Foo, или объявление функции foo() без параметров, возвращающей значение типа Foo?
veslemay
Паскаль-стиль не может быть единобразным — он слишком слабый даже для языков уровня js.
И в чём проблема?
Расскажите мне про прикол с:
(a, b) => c — что это?
А про [a, b, c]?
К тому же, очень наивно ссылаться на проблему, которая вообще не проблема и которую уже решили. И об этом написано по ссылке.
creker
Тем, что таким образом упрощается парсинг языка и исключаются неоднозначные ситуации, которых в С/С++ предостаточно. Первым всегда идет или ключевое слово или идентификатор. Тоже самое с переносом возвращаемого значения функции в конец и использованием ключевого слова вроде func — упрощает парсинг и делает адекватным использование функций первого порядка. В отличие от упомянутых С/С++, где это пытка сплошная.
veslemay
Кому, кроме студентов, интересна сложность парсинга? Язык должен быть мощным и удобным, а насколько его сложно парсить — никому не интересно.
Пустой лозунг, который я уже опровергал выше.
Опять лозунг. Хорошо оно идёт — дальше что?
Парсинг никому не интересен. Чем это использование более адекватно? Что вообще это значит. Опять какой-то лозунг без примеров/объяснений.
Опять же, в чём заключается пытка?
creker
Всем, кто потом будет работать с инструментами, которые язык парсят и что-то делают с AST. Таких для Go множество и именно благодаря его однозначному и простому синтаксису. Это ускоряет и сам процесс парсинга, что полезно вообще всем.
Никакого опровержения не заметил. Отсутствие неоднозначности в синтаксисе языке несет за собой далеко идущие преимущества. И Go этому яркий пример.
То, что парсить легко. Дальше логику сами можете достроить.
В том, что в С/С++ синтаксис указателя на функцию ужасен, нечитабелен, сложен для написания и полностью отличается от того, как функции объявляются. Чего стоит только необходимость включения имени переменной в сам тип вроде int(*foo)(). В Go объявление функции, объявления типа указателя на функцию, передача функции как объекта первого порядка — все это идентичные синтаксически вещи, что значительно упрощает чтение и написание кода. Авторы Go к этому стремились, делая такой синтаксис, и они своего добились. Помимо этого, С++, будучи заложником своей извращенной грамматики, придумал тот ужас, которым называют лямбды в этом языке. Тоже самое в C# том же, хоть и не настолько ужасно.
Вы уперлись в какие-то ваши странные представления о том, какой должен быть язык, не понимая, что С/С++ и им подобные это одни из худших примеров с точки зрения синтаксиса. Поэтому не случайно языки нынче выбирают совершенно иную логику, в том числе «идентификатор: тип», func/fun перед функцией и прочее и прочее. Люди наелись проблем этих языков и не хотят тащить теже самые проблем себе, только потому что все привыкли и вроде как все в порядке уже.
veslemay
Попросту враньё. Инструментов по статическому анализу С++-кода больше и они куда как мощнее и качественнее, чем для го. Никакой С++ парсить ненужно.
К тому же, это враньё ещё и тем, что в го нужны парсеры потому, что язык крайне примитивен и ничего не может. От того и нужнается в кодогене и тысячах костылях, для обхода отсутствия тех же генериков.
Т.е. у вас ничего, кроме лозунгов, нет? Где преимущества, в чём преимущества? Го в сравнении с тем же жаваскриптом/тайпскриптом — пыль. Я уж не говорю про С/С++. Что мне показывает пример го? Да ничего.
Ну то, что ваши тезисы — глупые лозунги я понял уже давно. Вы не на баррикадах и лозунги в данном случае котируются мало.
Примеры. Зачем мне лозунги? Конкретно. Берёте идентичный код и пишите его на го и на С++.
Опять лозунги?
Вы ведь ничего не знаете. Функция объявляется так же.
У вас явно лозунги плывут. Функции в си записывается как int(foo)(); Указатель записывается как int(* foo)(); То, что вы этого не знаете — это проблему. Узнайте/спросите перед тем, как делать далеко идущие заявления.
Опять лозунги. Приведите пример.
Это настолько нелепые заявления. Синтаксис го не может и 10% того, что может синтаксис си в С++, в том же C#. Зачем сравнивать несравнимое?
Это примерно как рассказывать, что у мышки 3 кнопки, а у клавиатуры 100. Вот ведь какая клавиатура плохая — кнопок много. Авторы мышки стремились сделать такое кол-во кнопок(малое) и они добились этого.
Все ваши рассуждения — пустые общие слова, которые ничего не значат.
Она извращённая лишь потому, что вы где-то это услышали? Сильная аргументация.
И да, у вас опять лозунг потёк. Покажите мне эквивалент С++-лямбды на го, либо на другом языке.
IvanNochnoy
Есть другой подход: compiler as a service, как это сделано в C#/TypeScript. В этом случае необходимость писать собственные парсеры отпадает, равно как и намеренно упрощать синтаксис.
veslemay
Именно этим и занимается clang уже 10лет. Да и уже лет 20 есть gcc-xml, которого для базовых вещей вполне хватит. Причём лучше, чем упомянутые выше случаи.
Тут двойной факап. И упоминания С++, который находится в авангарде «compiler as a service» и догматическая уверенность в одном избранном случае.
dimAdmin
Паскаль же вроде давно придумали, зачем снова разводить «зоопарк»???
NotThatEasy
Вообще, симпатичный синтаксис, однако, рекурсия тут задумана как некий «Unsafe» элемент… Стоп, что? Язык, претендующий на функциональность со всеми этими иммутабельностью, пайплайнами, вызовами фильтер/мап/редьюсов в ДжаваВосемь-стиле, только через стрелочки, и этот язык предлагает огромную кодобазу отмечать как рекурсивную, хотя мне не припоминается, чтоб у компиляторов когда-либо были проблемы с распознаванием рекурсивных вызовов в коде, с тех пор как в них добавляли поддержку рекурсивных вызовов.
Крайне занятная задумка со стринг-типами, хотя слишком сыро и конкретно – надо расширить на систему типов по возможности (я вижу отличное применение такому функционалу – он вполне может
потеснитькостыльнуть Хаскелльные тайпклассы, пусть и работает в другую сторону, так же, как и концепты из С++20).Также интересно отсутствие UB, но это не гарантирует, что программа будет себя вести, как задумано. Более того, никто ещё не видел ошибки реальных программ, а это очень важно – всегда придерживался и придерживаюсь мнения, что максимум ошибок надо ловить в компайлтайме.
Общее впечатление: симпатичный синтаксис (а больше пока ничего и нет), вангую проприетарный майкрософтовский F# x переRust.
NotThatEasy
P.S.: Насчёт ссылочности и сохранения иммутабельности, как по мне, всё просто: пример кода в статье на эту тему – негодный и не должен ни компилиться, ни, тем более, запускаться. В сигнатуре функции надо обязать указывать
mutablevar для изменяемых переменных. При правильно прикрученной системе вывода типов получим синтаксис функции вроде:function foo(var ref bar/*Тип указан только для второй переменной, в то время, как тип первой выводится*/, ref strategy: Int) /*Тип возвращаемого значения выводится сам*/
{
bar += 1;
//Ошибочный код:
//strategy += 1;
return bar;
}
Что в принципе миленько – возможность контроля типов получаемых и возвращаемых значений функции, как и их модификаторов.
mayorovp
А в чём смысл дублирования слова ref словом var? И почему нельзя написать просто
strategy: Int
, без ref?NotThatEasy
ref – Указание, что принимаемый тип будет ссылочным, а var же указывает на мутабельность.
Я провожу аналогию в меру своих знаний, а именно:
На плюсах можно написать:
/*void foo*/
(int a)
(int& a)
(const int& a)
Что будет значить три разных вещи. 1 – int скопируется новой переменной. 2 – будет передан мутабельный параметр по ссылке. 3 – передастся ссылка на константную (то есть, неизменяемая, она же мутабельная) переменную (константную ссылку на переменную, но не суть).
Имеем:
ref strategy: Int –> const int& strategy
strategy: Int –> const int strategy
var ref bar –> template T& bar
ref bar –> template const T& bar
Почему я использую ref для strategy – привычка, больше скажу, я консты не всегда пишу, и потому всеми конечностями за иммутабельность по умолчанию
mayorovp
Передача по константной ссылке отличается от передачи по значению с точки зрения поведения лишь в одном случае — когда параметр передаётся по константной ссылке и по неконстантной ссылке одновременно. Но такая передача — источник ошибок, и в том же Rust не просто так её запретили.
В том же C++ константные ссылки используются, как правило, для оптимизации; но в современном языке такую оптимизацию лучше поручить компилятору.
Таким образом, константные ссылки — не нужны, и остаётся всего два типа передачи параметра: по значению и по изменяемой ссылке. Именно второй способ и достигается при помощи слова ref.
Static_electro
Мне кажется, вы здесь излишне упрощаете, потому что в С++ константные ссылки используются еще и как гарантия той самой иммутабельности при отсутствии сайд-эффектов от копирования.
Т.е. если у меня на руках «тяжелый» константный объект, который хочется отдать как параметр в функцию, то константные ссылки — это хорошая вещь.
creker
Они работают только в одну сторону — чтобы получатель не изменил что-то по ссылке. А вот получателю нет никаких гарантий, что объект «иммутабельный» внезапно из стула не превратится в верблюда, потому что кто-то снаружи что-то внутри него дернул. По сути, это делает бесполезной всю эту затею, почему давно const модификатор скорее плохим тоном и мусором считается, которые постоянно мешает, чем реально полезным инструментом.
Static_electro
Хм. Интересно, можно сыылки на «плохой тон и мусор»? Я на полном серьезе, любопытно посмотреть.
creker
Ссылки не смогу. Это, так сказать, мое агрегированное мнение после чтения всего и вся. Я тоже не из ненависти какой-то, но много раз встречал в самых разных источниках обсуждение проблемы const, какой от него реально профит или вред. И как-то сложилось мнение, что, действительно, оно того не стоит и больше проблем создает. Модные нынче языки тоже вливаются в это русло, либо полностью исключив подобные костыли, либо внедрив себе полноценную иммутабельность в том или ином виде.
NotThatEasy
Ни разу не встречал обсуждение «проблемы const», Вами так с лёгкостью окрещённой, однако, могу представить Вам пример полноценной иммутабельности на С++:
int main(void)
{
const int i = 0;
++i;
//Всё как мы и ожидали, ошибка компиляции «expression must be a modifiable value»
}
Const – имхо не самая сложная тема при изучении С-подобных языков.
creker
Я и не говорю, что сложная. Но неоднозначная это точно, особенно в последнее время. Именно в разрезе const модификатора аргументов и функций. const переменная для реально константных выражений это как раз таки хороший тон. Тут даже иммутабельность не при чем уже.
mayorovp
Так с точки зрения вызывающего кода с const и правда никаких проблем, проблемы начинаются у вызываемого кода, получившего константную ссылку…
Static_electro
Из очевидных проблем могу назвать const_cast & mutable. Но что есть — то есть, вот с таким сиплюплюсом мы живем. Имхо, это не делает написание конст-корректного кода бесполезным занятием. А встретив на код-ревью эти попытки обмануть конст, можно смело начинать спрашивать о причинах.
NotThatEasy
Если во внешнем скоупе объект иммутабельный, то такой проблемы и не будет (бишь, функциональный подход для меня по большей части в этом и заключается), а с неизменяемостью по умолчанию – не придётся вообще писать
mutablevar.creker
Ну т.е. это совсем не const решается и не может решаться. Это либо весь язык таким должен быть, либо интерфейс у объектов такой, что они иммутабельны по-умолчанию. А так получается ни туда, ни сюда.
NotThatEasy
Вы апеллируете к многопоточному программированию?
В таком случае, это вопрос организации взаимодействия и разделения данных между потоками.
Решил продемонстрировать, как я это вижу:
В такой ситуации закомментированный const как раз снимает вопрос.
creker
Да даже без многопоточного. Достаточно положить объект себе куда-то в поле до лучших времен. Что с ним за это время может натворить мир вокруг никто не знает.
Static_electro
Так идея в том, что если ты получаешь конст-объект, то это ты не должен с ним что-то натворить.
creker
Это уже извращения из стана плюсов. Если мы говорим об иммутабельности, а об этом и начался разговор, то этого недостаточно. Это не иммутабельность и const вроде бы никогда ей не считался. Это модификатор доступа, но не характеристика иммутабельности объекта.
NotThatEasy
Дело в количестве потоков исполнения и они есть, если я не ошибаюсь, во всех языках общего назначения, а не только в спп:
Если он 1 – то программа выполняется построчно, и пока поток выполнения находится в функции, объект назван его формальным параметром, то есть условно const int& x.
Если же их более, чем 1 – переменная действительно может измениться, ну тут уже смотрите, чтоб не было гонок.
Мы обсуждаем странное понятие иммутабельности – позвольте уточнить, что иммутабельные данные – это не коробка байт, которые не изменятся, даже если подача тока прекратится/коротнёт. Это коробка байт, которую можно открыть и посмотреть, но не трогать байты руками.
Если же мы обсуждаем изменение данных «извне» (напр., тупую ассемблерную подмену данных тем же артмани), то для того, чтоб программа корректно(?!) обрабатывала такие изменения, придётся использовать продвинутые техники.
creker
Иммутабельность это иммутабельность. Это объект, который, единожды созданный, больше не изменяется. По крайней мере в рамках своего внешнего интерфейса. Нужно что-то изменить — вперед, делаем копию с изменением. Вот такая иммутабельность делает асинхронный код безопасным. А const модификатор это ограничение доступа к объекту, который, при этом, может меняться изменяться сколько влезет.
И чего вы к потокам привязались. Потоков может быть один, но код асинхронным. И запросто получишь ситуации, когда у тебя под ногами изменился объект. В современных языках с async/await и всеми этими вашими горутинами это обычное дело.
NotThatEasy
Асинхронный пример принят, сам как-то не подумал.
Непонятно следующее: каким образом /*фактическая*/ переменная, объявленная со словом const, может быть изменена в процессе выполнения программы?
Большинство современных компиляторов плюсов выдадут ошибку, если в коде её значение попытаться изменить.
mayorovp
Потоки тут ни при чём. Берем функцию add_mul, с сигнатурой
void add_mul(big_number &s, big_number const& a, big_number const &b) // s += a*b
(на олимпиадах я такую любил, ибо элементарная операция), пишемadd_mul(x, x, y) // x += x*y
и приплыли. Поток всего один, но это не помогло.mayorovp
А хотелось бы ошибку компиляции при "не снятом" вопросе...
Speakon
Как скоро появятся объявления рекрутёров с фразой «Опыт написания программ на Bosque от года»? ;)
sumanai
Меня больше бы позабавила эта фраза в резюме.
OlegTar
«от 5 лет»
VGoudkov
Kotlin? — нет, не слышали. Впрочем, у него есть фатальный недостаток
RedCatX
Всякий раз, когда очередной язык хотят сделать «более понятным для человека», они просто приближают его синтаксис к чему-то паскалеподобному. Так почему бы просто не взять Паскаль?
math_coder
Там переменные надо объявлять в отдельном блоке. Есть мазохисты, которым нравится, но остальные сильно против. Форвард-декларации — то же самое. И ещё много моментов, неадекватных с современной точки зрения.
RedCatX
Объявлять переменные в отдельном блоке не очень удобно при написании кода, но читать и понимать такой код потом значительно легче.
TheKnight
Вопрос привычки и используемых инструментов, на самом то деле.
Мне удобней читать когда переменные объявлены рядом с местом их использования.
mayorovp
Не легче. В этом самом отдельном блоке у переменной есть только имя и тип, и никакого контекста.
skovpen
а тут — перлом попахивает
third112
sumanai
Вот тут в табличке выше есть.
surly
Мне нравится, как сделано в языке R. Можно произвольно сочетать позиционную и именованную запись. В то время как в большинстве других языков именованные аргументы можно перечислять только после позиционных.
ksbes
Kotlin? В нём, кстати, почти всё перечисленное в статье уже есть и выглядит весьма органично и дружелюбно, в отличии от.
Чувствуется, MS решили запилить свой K# (как они запилили С# в ответ на Java). По мне — так лучше бы F# развивали, «отвязав» его от необходимости быть «совместимым» с С#.
third112
А если к этому добавить параметры по умолчанию:
то можно сделать вызова совсем роскошно будет выглядеть эквивалентный вызов
для параметра с уникальным типом в данной функции.
shikhalev
Простите, а анонс точно не 1 апреля вышел?
Shamanay
Теперь можно пить шампанское «Боско» и писать на языке «Боско».
Bytamine
Страшен, как образы на картинах Босха.
vba
Ну наконец-то они создали такой язык (F# не в счет).