[JavaScript, Программирование, Я пиарюсь, Lisp] Как я устал от JavaScript и создал свой собственный язык программирования
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
За свою карьеру я успел поработать со множеством языков программирования. Писал flash-игры на ActionScript 3 и Android-игры на Java, сервера на Java, Scala и NodeJS (JavaScript), скрипты на Python, веб и мобильные приложения на React (JavaScript). И на каком бы языке я не писал, меня не покидало ощущение, что синтаксис этого языка слишком многословен, полон излишеств, шума и синтаксического бойлерплейта, мешающего пониманию написанного кода. Уже тогда я начал делать первые попытки создать свой собственный язык программирования, лишенный этих недостатков. Но для начала мне надо было определиться с тем, что именно не так с существующими языками и каким образом можно формализовать параметры "качества" языка, чтобы начать их улучшать. После долгих размышлений и сравнений я выработал для себя несколько параметров:
- Человекочитаемость - идеальный язык должен быть читабельным и легким для понимания человеком
- Лаконичность - код на идеальном языке программирования должен содержать как можно больше смысла на квадратный сантиметр экрана. Максимум семантики, минимум синтаксиса.
- Внутренняя непротиворечивость - синтаксические конструкции языка должны быть внутренне согласованы и хорошо сочетаемы
- Чистота и красота - язык должен выглядеть максимально чисто и эстетически красиво
Я брал за основу разные языки программирования и, как скульптор высекающий из камня прекрасную статую, начинал создавать из взятого за основу языка свой идеал, постепенно улучшая каждый из определенных мною параметров качества. И какой бы язык я не брал за основу, в конце-концов я всегда получал одно и то же - Lisp. Единственным параметром, которому не до конца соответствовал Lisp, был параметр чистоты и красоты. Уходящие за горизонт заборы из скобочек не нравились мне с точки зрения эстетики. И я нашел решение: чтобы уменьшить количество скобочек можно сделать значимой индентацию, как это сделано в Python. Я продолжал заниматься своими экспериментами в приватном репозитории гитхаба, то забрасывая их, то снова к ним возвращаясь. А тем временем моим основным рабочим языком становился JavaScript. Как я перестал бояться и полюбил JavaScriptИзначально я не любил JavaScript. Не потому что в нем было что-то не так, а просто потому что я подвергся влиянию популярного мнения, что JavaScript - это неполноценный язык для дурачков, а настоящие программисты пишут только на "тру" языках. Незадолго до знакомства с JavaScript моим основным рабочим языком была Scala. Вот он-то мне казался по-настоящему крутым, ибо он был полон сверхсложной эквилибристки типов и даже для написания простейшего http-сервера на Scala требовался высший пилотаж.Поэтому когда я открыл для себя NodeJS, я сначала не поверил, что можно писать так просто и лаконично. И чем дольше я знакомился с JavaScript, тем более гениально спроектированным и внутренне согласованным он мне казался. Система объектов и прототипов позволяет легко писать код в объектно-ориентированном стиле, а то, что в основе всего этого лежит функция, делает легким и приятным программирование в привычном мне функциональном стиле. А динамическая природа языка делает его фантастически гибким.Кроме того в экосистеме JavaScript меня поразило обилие инструментов и хороших библиотек. В Scala в порядке вещей была ситуация, когда библиотека была написана для работы над PhD и заброшена после защиты диссертации. В Java библиотеки были в основном рабочими, но многие из них были абсолютно не дружелюбными - лежали не пойми где, не имели документации в нормальном виде, имели ужасный API и так далее. У каждого языка была своя ужасающая система сборки и управления зависимостями вроде maven, gradle или sbt. В JavaScript же программисту доступен удобный пакетный-менеджер npm, полный прекрасно отлаженных библиотек с хорошей документацией, удобным API и дружелюбным коммьюнити. То, что кажется обыденностью в экосистеме JavaScript, в экосистемах других языков не является таковым: например, не во всех случаях является возможным понять что же делает какая-нибудь Ruby-библиотека по ее readme в гитхабе.Отдельным преимуществом JavaScript является то, что сейчас его можно запустить где угодно. Для веб-приложений уже давно стандартом является React, на мобильных девайсах очень распространен React Native, на сервере бешено популярен NodeJS. JavaScript стал де-факто универсальным языком, на котором можно писать под что-угодно. Даже миллиарды микроволновок и утюгов, которые раньше были вотчиной Java, скоро все будут программироваться на JavaScript. Но есть ли у JS какие-нибудь минусы?Что не так с JavaScriptЯ программирую на JavaScript, в основном, в чисто функциональном стиле. Я использую const, не мутирую объекты и массивы, использую чистые функции. Так что мой код выглядит примерно так:
const incrementNumbers = numbers => numbers.map(number => number + 1)
const takeNumbersGreaterThan = threshold => numbers => numbers.filter(number => number > threshold)
const func = (numbers, threshold) => {
const incrementedNumbers = incrementNumbers(numbers)
const filteredNumbers = takeNumbersGreaterThan(threshold)(incrementedNumbers)
return incrementedNumbers
}
Не смотрите, что этот код является переусложенным и может быть написан в разы проще. Здесь я хочу продемонстрировать то, что если вы как и я пишите код на JavaScript в исключительно функциональном стиле, то кучу места на вашем экране занимает абсолютно ненужный вам синтаксический мусор вроде слова "const", когда все и так всегда константа, и фигурных скобочек, когда все и так разделено индентацией.Собственно, именно эти недостатки и натолкнули меня на возвращение к проекту собственного языка программирования. Ну а из-за описанных мною ранее громадных преимуществ экосистемы именно JavaScript я сделал платформой для своего языка.Это было долгое вступление, но давайте уже начнем знакомство с языком. Представляю вам мой уникальный, универсальный и унифицированный язык программирования Una.Порядок выполнения операцийДавайте начнем с азов. А именно порядка выполнения операций. Как я писал выше, основой синтаксиса для моего языка послужил Lisp, но чтобы сократить количество скобочек, я решил использовать индентацию.Поэтому в моем языке выражение:
a (b c) (d e)
можно также записать как:
a
b c
d e
Ну а выражение:
a (b (c d))
может быть записано так:
a
b (c d)
или так:
a
b
c d
ПрисваиваниеСамый базовый оператор в любом языке программирования - это оператор присваивания. В случае функциональных иммутабельных языков программирования правильнее было бы сказать не присваивание, а объявление константы.В Una константа объявляется так:
= name "John"
= number 1
Если мы хотим присвоить константе значение вычислений, то делаем так:
= z (calculate x y)
а можно обойтись и без скобок, этот оператор позволяет сделать так:
= z calculate x y
или так
= z calclulate
x
y
Оператор присваивания в Una берет значение второго параметра и присваивает его первому. Если параметров больше двух, то он сначала применяет второй параметр ко всем последующим, а затем присваивает результат выполнения операции первому.Арифметика, сравнения и логикаВ Una вы можете пользоваться стандартными JavaScript операторами арифметики, сравнения и логики.Арифметические операции выглядят так:
= a (+ 1 2)
= b (- 2 1)
= c (* 3 2)
= d (/ 4 2)
= e (% 5 2)
Все из них могут принимать множество параметров. Например:
= sum (+ 1 2 3 4 5)
А минус может принимать и один параметр, что является отрицанием числа:
= a (- 1)
= b -1
Вот пример сложных арифметических расчетов:
= a +
* 2 4
/ 9 3
+ (* 3 2) (/ 4 2)
*
+ 1 2
/ 6 3
Конечно также вы можете использовать любые функции из стандартной библиотеки JS:
= randomNumber Math.random ()
Операторы сравнения имеют некоторые отличия от JavaScript. Давайте посмотрим:
= a (== 1 1)
= b (~= 1 '1')
= c (!= 1 '1')
= d (!~= 1 '2')
= e (> 2 1)
= f (>= 2 1)
= g (< 1 2)
= h (<= 1 2)
Оператор == и != - это точное по типу сравнение, аналог === и !== в JavaScript. Для неточного по типу сравнения нужно применять ~= и !~=.Логические операторы тоже немного отличаются:
= a (& true false)
= b (| true false)
= c (! true)
= d !c
Как вы видите, вместо && используется &, а вместо || используется |.Условные операторыТернарный условный оператор работает также как и в JavaScript:
= message
? (> 2 1) "Greater" "Less"
А еще существует условный оператор с возвращением значения:
?! (== number 1) "One"
Он используется в функцях и других вычислимых блоках, чтобы вернуть значение по условию и соответствует следующему коду на JavaScript:
if (number === 1) return "One"
КоллекцииВ Una есть две базовые коллекции: массивы и объекты. Массив задается следующим образом:
= numbers :: 1 2 3 4 5
А объект задается так:
= user :
name 'John'
age 13
passport :
id 1
country 'USA'
В Una работают все трюки JavaScript. Можно задать элемент массива или поле объекта готовой константой:
= a 1
= numbers :: a 2 3
= name 'John'
= user :
name
age 13
Работает троеточие:
= threeNumbers :: 1 2 3
= fiveNumbers :: ...threeNumbers 45
= userFields :
name 'John'
age 13
= user :
id 1
gender 'm'
isAlive true
...userFields
Работает деконструкция:
= numbers :: 1 2 3
= (:: one two three) numbers
console.log one
= user : (name 'John') (age 12)
= (: name) user
console.log name
А чтобы взять определенный элемент массива или значение определенного поля объекта используется точка:
= list :: 1 2 3
= object : (a 1) (b 2)
console.log (. list 0)
console.log (. object 'a')
Также точка может быть использована, чтобы взять конкретное поле объекта таким образом:
= object : (a 1)
console.log (.a object)
Этот синтаксис удобен для вызова функций, лежащих внутри объекта.Но давайте пойдем дальше и рассмотрим самую крутую фишку языка - стрелочные симметрии.Стрелочная симметрия синхронных вычисленийПравая стрелка симметрии синхронных вычислений является функцией:
= sum -> (x y)
+ x y
= onePlusTwo -> ()
= one 1
= two 2
+ one two
Последняя строчка функции всегда является возвращаемым значением. Данный код на JavaScript будет выглядить так:
const sum = (x, y) => x + y
const onePlusTwo = () => {
const one = 1
const two = 2
return one + two
}
Для преждевременного прерывания выполнения функции можно использовать вышеупомянутый условный оператор:
= isSumGreaterThanFiveIfXisNotZero -> (x y)
?! (== x 0) "X is zero"
= sum (+ x y)
? (> sum 5)
"Greater"
"Less"
Левая стрелка симметрии синхронных вычислений является мнгновенно выполняемым блоком кода, также называемым Immediately Invoked Function Expression:
= result <-
= a 1
= b 2
+ a b
Этот оператор можно использовать для чего-то вроде задания константы значением по каким-то условиям:
= result <-
?! (== value 0) "Zero"
?! (== value 1) "One"
? (< value 10) "Less than ten" "More than ten"
Также его можно использовать для того, чтобы выполнить какой-либо код в теле условного оператора. Например, вывести что-то в консоль по какому-либо условию:
= func -> number
? (== number 0)
<-
console.log "Number is zero!"
+ number 1
Стрелочная симметрия асинхронных вычисленийПравой стрелкой симметрии асинхронных вычислений является асинхронная функция:
= getUserPosts --> user
database.loadPosts user.postIds
Левой стрелкой симметрии асинхронных вычислений является await:
= checkIfUserIsAdmin --> userId
= user <-- (database.loadUser userId)
== user.role 'admin'
Стрелочная симметрия модулейПравой стрелкой модульной симметрии является импорт:
=-> './index.css'
=-> 'react' React
=-> './validation' (: validateEmail)
Он превращается в import или в require в зависимости от соответствующей настройки компилятора.Левой стрелкой модульной симметрии является экспорт.Дефолтный - export default в JavaScript:
<-= a
Экспорт отдельной константы export const в JavaScript:
<-= = a 1
Экспорт нескольких констант - export {a, b} в JavaScript:
<-= ()
a
b
Вы можете импортировать любые JavaScript-модули в Una, и любые Una-модули в JavaScript. Все модули полностью совместимы друг с другом.Стрелочная симметрия ошибокПравой стрелкой симметрии ошибок является try-catch:
|->
<-
= getName null
getName ()
-> error
console.log error
'John'
В отличие от JavaScript в Una этот оператор является выражением, всегда возвращающим значение, а также этот оператор не имеет блока finally.Если нужно исполнить асинхронный код, то соответствующие стрелки симметрии заменяются на асинхронные:
|->
<--
getNameAsync ()
--> error
console.log error
"John"
Левой стрелкой симметрии ошибок является выброс ошибки:
= addOneToNumber -> number
?! (isNaN number)
<-| "number is not valid"
+ number 1
Стрелочная симметрия чейнингаПравая стрелка симметрии чейнинга подставляет выражение как последний параметр следующей за ним функции. Это нужно для удобной работы с такими библиотеками как ramda:
=-> 'ramda' R
= electronics ::
:
title ' iPhone '
type 'phone'
= phones |>
electronics
R.find
R.propEq 'type' 'phone'
R.prop 'title'
R.toUpper
R.trim
Левая стрелка симметрии чейнинга подставляет выражение как первый параметр следующей за ним функции. Это удобно для работу со стандартными методами массивов и lodash:
= sum <| (:: 1 2 3)
.map (-> x (+ x 1))
.filter (-> x (> x 2))
.reduce (-> (x y) (+ x y)) 0
Без чейнинга это выражение выглядело бы уродски:
= sum .reduce
.filter
.map (:: 1 2 3) (-> x (+ x 1))
-> x (> x 2)
-> (x y) (+ x y)
0
Интерполяция строкОдной из самых популярных возможностей современный языков программирования является интерполяция строк. В Una она выглядит не так красиво как в JavaScript, но все же пользоваться ею очень удобно:
= name 'John'
= count 5
= fruit 'apples'
= text `
'Hello, ${0}' name
'I have ${0} ${1}'
count
fruit
console.log text
Этот пример выведет:
Hello, John
I have 5 apples
Так же вы можете провести интерполяцию строк по какой-либо функции - чтобы сделать это пришлите функцию первым параметром. Это очень часто используется например в styled-components:
= color 'red'
= Container `
styled.div
'background-color: ${0};' color
React и React NativeДля написания веб-приложения на React или мобильного приложения на React Native вы не сможете использовать JSX. Вместо этого вы можете использовать функцию createElement, лежащую в основе React.
=-> './index.css'
=-> 'react' React
=-> 'react-dom' ReactDOM
=-> './styles' S
= (: (createElement e)) React
= App -> ((: name))
= (:: count setCount) (React.useState 0)
e S.Container :
e S.Hello (: (color 'green')) 'Hello, '
e S.Name : name
e S.IncrementCount
: (onClick (-> () (setCount (+ count 1))))
'Press me'
e S.Count : count
ReactDOM.render
e App (: (name 'John'))
document.getElementById 'root'
Первым аргументом функция всегда принимает компонент, вторым объект с параметрами (для пустого объекта в Una вы можете использовать : ), а все последующие параметры - это chilldren.Что еще предстоит сделатьЯзык еще не закончен, многое еще предстоит. В скором будущем я постараюсь сделать:
- регулярные выражения
- возможность заинстансить класс (оператор new)
- комментарии в коде
- плагин для Visual Studio Code с подсветкой синтаксиса
- и многое другое...
ЗаключениеЯзык Una устанавливается в любой JavaScript-проект простейшим babel-плагином, который компилирует все файлы с расширением .una в JavaScript. Вы можете найти инструкции по установке, подробную документацию и примеры в моем репозитории на гитхабе. Я буду жутко рад, если вы поиграетесь с языком, протестируете его и дадите фидбек, что можно было бы улучшить, а что стоит починить. Ну и от звездочек конечно не откажусь.
Спасибо за внимание!
===========
Источник:
habr.com
===========
Похожие новости:
- [JavaScript, HTML, Node.JS] Написание графического приложения на Electron JS (начало: Создание окна)
- [Программирование, Data Mining, ООП, R, Data Engineering] ООП в языке R (часть 2): R6 классы
- [Разработка веб-сайтов, JavaScript, Программирование] Пример практического использования модулей
- [Промышленное программирование, Разработка робототехники, Программирование микроконтроллеров, Производство и разработка электроники] ModBus Slave RTU/ASCII без смс и регистрации. Версия 3
- [Высокая производительность, Разработка веб-сайтов, JavaScript, Клиентская оптимизация, ReactJS] Производительность приложений, работающих с Video и Audio
- [Разработка веб-сайтов, JavaScript, VueJS] Автоматическое обновление скриптов после деплоя
- [Python, Программирование] Напишем и поймем Decision Tree на Python с нуля! Часть 5. Информационная энтропия (перевод)
- [Мессенджеры, JavaScript, Программирование, VueJS] Как создать приложение-чат за двадцать минут (перевод)
- [Разработка веб-сайтов, JavaScript, Программирование] Как анимировать элемент «details» с помощью WAAPI (перевод)
- [Программирование, Совершенный код, C++, C] Пока смерть не разлучит нас или всё о static в C++
Теги для поиска: #_javascript, #_programmirovanie (Программирование), #_ja_piarjus (Я пиарюсь), #_lisp, #_una, #_jazyk_programmirovanija (язык программирования), #_funktsionalnyj_jazyk (функциональный язык), #_funktsionalnyj_javascript (функциональный javascript), #_javascript, #_lisp, #_javascript, #_programmirovanie (
Программирование
), #_ja_piarjus (
Я пиарюсь
), #_lisp
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 25-Ноя 05:39
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
За свою карьеру я успел поработать со множеством языков программирования. Писал flash-игры на ActionScript 3 и Android-игры на Java, сервера на Java, Scala и NodeJS (JavaScript), скрипты на Python, веб и мобильные приложения на React (JavaScript). И на каком бы языке я не писал, меня не покидало ощущение, что синтаксис этого языка слишком многословен, полон излишеств, шума и синтаксического бойлерплейта, мешающего пониманию написанного кода. Уже тогда я начал делать первые попытки создать свой собственный язык программирования, лишенный этих недостатков. Но для начала мне надо было определиться с тем, что именно не так с существующими языками и каким образом можно формализовать параметры "качества" языка, чтобы начать их улучшать. После долгих размышлений и сравнений я выработал для себя несколько параметров:
const incrementNumbers = numbers => numbers.map(number => number + 1)
const takeNumbersGreaterThan = threshold => numbers => numbers.filter(number => number > threshold) const func = (numbers, threshold) => { const incrementedNumbers = incrementNumbers(numbers) const filteredNumbers = takeNumbersGreaterThan(threshold)(incrementedNumbers) return incrementedNumbers } a (b c) (d e)
a
b c d e a (b (c d))
a
b (c d) a
b c d = name "John"
= number 1 = z (calculate x y)
= z calculate x y
= z calclulate
x y = a (+ 1 2)
= b (- 2 1) = c (* 3 2) = d (/ 4 2) = e (% 5 2) = sum (+ 1 2 3 4 5)
= a (- 1)
= b -1 = a +
* 2 4 / 9 3 + (* 3 2) (/ 4 2) * + 1 2 / 6 3 = randomNumber Math.random ()
= a (== 1 1)
= b (~= 1 '1') = c (!= 1 '1') = d (!~= 1 '2') = e (> 2 1) = f (>= 2 1) = g (< 1 2) = h (<= 1 2) = a (& true false)
= b (| true false) = c (! true) = d !c = message
? (> 2 1) "Greater" "Less" ?! (== number 1) "One"
if (number === 1) return "One"
= numbers :: 1 2 3 4 5
= user :
name 'John' age 13 passport : id 1 country 'USA' = a 1
= numbers :: a 2 3 = name 'John' = user : name age 13 = threeNumbers :: 1 2 3
= fiveNumbers :: ...threeNumbers 45 = userFields : name 'John' age 13 = user : id 1 gender 'm' isAlive true ...userFields = numbers :: 1 2 3
= (:: one two three) numbers console.log one = user : (name 'John') (age 12) = (: name) user console.log name = list :: 1 2 3
= object : (a 1) (b 2) console.log (. list 0) console.log (. object 'a') = object : (a 1)
console.log (.a object) = sum -> (x y)
+ x y = onePlusTwo -> () = one 1 = two 2 + one two const sum = (x, y) => x + y
const onePlusTwo = () => { const one = 1 const two = 2 return one + two } = isSumGreaterThanFiveIfXisNotZero -> (x y)
?! (== x 0) "X is zero" = sum (+ x y) ? (> sum 5) "Greater" "Less" = result <-
= a 1 = b 2 + a b = result <-
?! (== value 0) "Zero" ?! (== value 1) "One" ? (< value 10) "Less than ten" "More than ten" = func -> number
? (== number 0) <- console.log "Number is zero!" + number 1 = getUserPosts --> user
database.loadPosts user.postIds = checkIfUserIsAdmin --> userId
= user <-- (database.loadUser userId) == user.role 'admin' =-> './index.css'
=-> 'react' React =-> './validation' (: validateEmail) <-= a
<-= = a 1
<-= ()
a b |->
<- = getName null getName () -> error console.log error 'John' |->
<-- getNameAsync () --> error console.log error "John" = addOneToNumber -> number
?! (isNaN number) <-| "number is not valid" + number 1 =-> 'ramda' R
= electronics :: : title ' iPhone ' type 'phone' = phones |> electronics R.find R.propEq 'type' 'phone' R.prop 'title' R.toUpper R.trim = sum <| (:: 1 2 3)
.map (-> x (+ x 1)) .filter (-> x (> x 2)) .reduce (-> (x y) (+ x y)) 0 = sum .reduce
.filter .map (:: 1 2 3) (-> x (+ x 1)) -> x (> x 2) -> (x y) (+ x y) 0 = name 'John'
= count 5 = fruit 'apples' = text ` 'Hello, ${0}' name 'I have ${0} ${1}' count fruit console.log text Hello, John
I have 5 apples = color 'red'
= Container ` styled.div 'background-color: ${0};' color =-> './index.css'
=-> 'react' React =-> 'react-dom' ReactDOM =-> './styles' S = (: (createElement e)) React = App -> ((: name)) = (:: count setCount) (React.useState 0) e S.Container : e S.Hello (: (color 'green')) 'Hello, ' e S.Name : name e S.IncrementCount : (onClick (-> () (setCount (+ count 1)))) 'Press me' e S.Count : count ReactDOM.render e App (: (name 'John')) document.getElementById 'root'
Спасибо за внимание! =========== Источник: habr.com =========== Похожие новости:
Программирование ), #_ja_piarjus ( Я пиарюсь ), #_lisp |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 25-Ноя 05:39
Часовой пояс: UTC + 5