LINUX.ORG.RU

Racket 8.0

 , ,

Racket 8.0

0

5

Вышла новая мажорная версия языка программирования Racket, основанного на Scheme и нацеленного на создание пользователем собственных DSL.

  • Завершён переход на среду исполнения Chez Scheme — таким образом удалось уменьшить объём генерируемого кода на величину от 10 до 30%, а также значительно ускорить выполнение программ и повысить эффективность.
  • Переписан движок среды тестирования программ.

>>> Подробности

★★★★

Проверено: atsym ()
Последнее исправление: atsym (всего исправлений: 1)

Ответ на: комментарий от DarkEld3r

Вот и получается, что дело больше в мощности системы типов, ну и количестве сахара в языке.

Так я и пишу, что типы не мешают до тех пор, пока система типов позволяет описать задачу.

В Haskell, например, всё гораздо сложнее. Там объединение типов невозможно.

Если брать раст, то там время жизни в типе. Например, как решить

use std::thread;

fn main() {
    let v = vec![1, 2, 3];

    let handle = thread::spawn(|| {
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
    drop(v);
}

?

Если поток не создавать, то работает:

fn main() {
    let v = vec![1, 2, 3];

    let handle = || {
        println!("Here's a vector: {:?}", v);
    };

    handle();
    drop(v);
}

В Typed Racket не отображаются из обычного Racket call/cc или вызовы типа (map (lambda (x y z t) …) xx yy zz tt), где x y z и t имеют разные типы. Приходится руками дописывать «мамой клянусь, всё верно» для компилятора.

monk ★★★★★
()
Ответ на: комментарий от monk

грязное клятвопреступление не влияет на время жизни, тк в hs эта концепция отсутствует

anonymous
()
Ответ на: комментарий от monk

В Haskell, например, всё гораздо сложнее. Там объединение типов невозможно.

Pазве как-то так сделать нельзя?

data Data = IntData Int | StringData String

Или речь о том, что не получится неявно приводить Int к Data? Не уверен, что это прям плохо.

Если брать раст, то там время жизни в типе. Например, как решить

Смотря, что нужно. Если вектор больше не нужен, то можно от дать его в поток. Если нужен доступ из разных потоков, то естественно нужно использовать механизмы синхронизации. Ну а если вдруг действительно нужно передать ссылку в поток и дождаться выполнения, то и это можно:

fn main() {
    let v = vec![1, 2, 3];

    thread::scope(|s| {
        s.spawn(|_| {
            println!("Here's a vector: {:?}", v);
        });
    })
    .unwrap();
    drop(v);
}
DarkEld3r ★★★★★
()
Последнее исправление: DarkEld3r (всего исправлений: 1)
Ответ на: комментарий от DarkEld3r

Или речь о том, что не получится неявно приводить Int к Data? Не уверен, что это прям плохо.

Именно про это. Благодаря этому каждое значение имеет ровно один тип и работает полиморфизм по типам.

Но если надо в функцию передать дополнительные данные, то проще написать новую функцию, чем изменить сигнатуру.

если вдруг действительно нужно передать ссылку в поток и дождаться выполнения, то и это можно

М-да. Надо бы найти время поизучать этого мутанта. Исходник crossbeam_utils::thread::scope дважды почитал, магию захвата внешних ссылок не понял.

monk ★★★★★
()
Ответ на: комментарий от monk

Именно про это. Благодаря этому каждое значение имеет ровно один тип и работает полиморфизм по типам.

После лиспа больше всего шокировало, что любой символ тоже имеет только один тип (и это не symbol): например, Nothing имеет тип Maybe. И написать в программе

data Data1 = S String | Fail
data Data2 = D Int | Fail

нельзя. Потом привык.

monk ★★★★★
()
Ответ на: комментарий от monk

Функциональщикам шалом, остальным соболезную :)

anonymous
()
Ответ на: комментарий от anonymous

тут думаю всем и так все понятно

Нет, но спасибо.

DarkEld3r ★★★★★
()
Ответ на: комментарий от monk

Именно про это. Благодаря этому каждое значение имеет ровно один тип и работает полиморфизм по типам.

Ну это не выглядит проблемой ведь всегда можно сделать Fail2?

DarkEld3r ★★★★★
()
Последнее исправление: DarkEld3r (всего исправлений: 1)
Ответ на: комментарий от monk

Исходник crossbeam_utils::thread::scope дважды почитал, магию захвата внешних ссылок не понял.

Если интересно разобраться, то, возможно, поможет понимание почему в crossbeam именно такое апи. В какой-то момент в стандартную библиотеку добавили (уже не помню успели стабилизировать или нет) апи scoped потоков. Выглядело это попроще, но проблема как в раз том, что в безопасном подмножестве языка можно устроить утечку и нельзя полагаться на то, что деструктор в плане гарантий безопасности. С деталями можно ознакомиться вот тут: https://github.com/rust-lang/rust/issues/24292

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Ну это не выглядит проблемой ведь всегда можно сделать Fail2?

Можно. Потом главное не забыть, что getAccount надо сравнивать с Fail42, а getBalance c Fail143. Или можно сделать

class MayFail a where failed :: a

Или можно сделать кучу пакетов и иметь Account.Fail, Balance.Fail, … Или можно вместо S String | Fail использовать Maybe String.

То есть, не то, чтобы нерешаемо, просто неудобно.

monk ★★★★★
()
Ответ на: комментарий от Siborgium

А ноги растут из отсутствия перегрузки.

Чем typeclass не перегрузка?

Ноги растут из необходимости однозначно определять тип по значению. Число 1 не может быть одновременно типа int и типа char как в С. Или иметь бесконечное число типов как в лиспах, паскале, Аде, …

monk ★★★★★
()
Ответ на: комментарий от monk

Ноги растут из необходимости однозначно определять тип по значению.

Ну вот нет такой необходимости. Точнее, есть, но в силу ограничений хаскеля и подобных. Тип значения отлично описывается самим значением.

Чем typeclass не перегрузка?

Тайпкласс не позволяет добавлять значения в overload set, не вписывающиеся в заданный им (тайпклассом) интерфейс.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от DarkEld3r

Если интересно разобраться, то, возможно, поможет понимание почему в crossbeam именно такое апи.

Да я не про апи, а про реализацию. Всё, нашёл, где магия: https://docs.rs/crossbeam-utils/0.6.0/src/crossbeam_utils/thread.rs.html#270

Я сначала, почему-то решил, что должно быть где то в https://docs.rs/crossbeam-utils/0.6.0/src/crossbeam_utils/thread.rs.html#113-158

monk ★★★★★
()
Ответ на: комментарий от Siborgium

overload set, не вписывающиеся в заданный им (тайпклассом) интерфейс

Так overload set именно интерфейсом тайпкласса и описывается. Не понял суть претензий.

Тип значения отлично описывается самим значением.

В Си, лиспах, аде — нет. В ООП языках значением определяется только класс, но не тип.

А в хаскеле это не ограничение, а возможность не указывать явно тип значений.

monk ★★★★★
()
Ответ на: комментарий от monk

Так overload set именно интерфейсом тайпкласса и описывается. Не понял суть претензий.

В хаскелле – да. В, например, крестах – нет. Частично это, конечно, проблема ограниченности тайпклассов.

А в хаскеле это не ограничение, а возможность не указывать явно тип значений.

Ну конечно это ограничение – ограничивается значение единственным допустимым типом/интерфейсом.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Siborgium

В, например, крестах – нет.

Можно пример перегрузки в крестах, которую нельзя сделать в хаскеле?

ограничивается значение единственным допустимым типом/интерфейсом

Это намеренно. Также как и невозможность смены класса объекта в С++.

И тип единственный, но интерфейсов у типа много: добавляются тайпклассами.

monk ★★★★★
()
Ответ на: комментарий от monk

Можно пример перегрузки в крестах, которую нельзя сделать в хаскеле?

По числу аргументов функции, например. По свойствам типа аргумента.

Это намеренно.

Только что это, по вашим словам, не было ограничением.

Также как и невозможность смены класса объекта в С++.

Да.

И тип единственный, но интерфейсов у типа много: добавляются тайпклассами.

Да, но какое это отношение имеет к

ограничивается значение единственным допустимым типом/интерфейсом.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

По числу аргументов функции, например

class ArgF a where
  f :: a

instance ArgF (String -> Int) where
  f s = length s

instance ArgF (Int -> String -> Char) where
  f i s = s !! i

По свойствам типа аргумента.

В смысле? Можно пример?

какое это отношение имеет к

Единственным типом ограничивается. Единственным допустимым интерфейсом не ограничивается.

monk ★★★★★
()
Ответ на: комментарий от monk

Число 1 не может быть одновременно типа int и типа char как в С

Так оно однозначно int. Остальное - приведения типов.

anonymous
()
Ответ на: комментарий от anonymous

Так оно однозначно int. Остальное - приведения типов.

Тогда какие значения относятся к типу signed char, например?

monk ★★★★★
()
Ответ на: комментарий от monk

Скорее всего, свойства типа аргумента – это типа вот таких констрейнтов в TypeScript:

const fn = <T, F extends (x: T) => boolean>(f: F) => {}

Благодаря которым мы можем передать функции такую функцию, которая могла бы помимо самого аргумента выведенного типа, ещё принимать какие-нибудь опции, конфиг например. Но в хаскелле такое делать можно.

Princesska ★★★★
() автор топика
Ответ на: комментарий от monk

Покажите примеры использования.

В смысле? Можно пример?

Например, я хочу перегрузить по признаку реализации шаблонным параметром какого-либо трейта, или, скажем, по sizeof(T). В целом вычисление каких-либо предикатов над типом и на основании этого включение функции в overload set. Как концепты/констрейнты в крестах.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 2)
Ответ на: комментарий от Siborgium

Покажите примеры использования.

main= do
  let len = f "ok" :: Int
  let n = 1 :: Int
  let c = f n "ok" :: Char
  putStrLn $ show len
  putStrLn $ show c

Например, я хочу перегрузить по признаку реализации шаблонным параметром какого-либо трейта,

instance Monoid a => Applicative ((,) a) where
   ...

или, скажем, по sizeof(T)

Можно сделать типы классов SizeOf1, SizeOf2, … Если нужны реально произвольные вычисления, можно через Template Haskell.

monk ★★★★★
()
Ответ на: комментарий от monk

Покажите примеры использования.

С явным указанием оверлоада не интересно. Хотя для конкретно хаскеля необходимость в этом понятна.

Можно сделать типы классов SizeOf1, SizeOf2

Бессмысленно. Даже для 1..256 уже получается чушь.

Если нужны реально произвольные вычисления, можно через Template Haskell

Да кто же спорит…

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Siborgium

Токен «1» в Си - это литерал. А число 1 - это допустимое значение любого целочисленного типа Си.

Также как в Haskell литерал 1 трактуется как значение любого типа класса Num. Но число 1 имеет тип Integer.

monk ★★★★★
()
Ответ на: комментарий от monk

Токен – это не литерал. Токен вообще к семантике программы отношения не имеет.

А число 1 - это допустимое значение любого целочисленного типа Си.

Это так, но число 1 само по себе отношения к типам данных не имеет. Оно с их помощью представляется.

Так вот, в С 1 – именно что литерал, при присвоении которого целочисленному типу данных выбирается подходящее представление в памяти.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

С явным указанием оверлоада не интересно.

Если использование будет в типизированном выражении, то можно без явного. В C++ тоже оверлоад всегда явный за счёт явной типизации переменных и литералов.

Бессмысленно. Даже для 1..256 уже получается чушь.

На практике видел сравнение не больше, чем с 8.

monk ★★★★★
()
Ответ на: комментарий от Siborgium

Это так, но число 1 само по себе отношения к типам данных не имеет.

Имеет

int c = 1;
switch(c)
{
  case 1:
}

и

char c = 1;
switch(c)
{
  case 1:
}

работают. Более того,

int c = 1;
char d = 1;
c == d 

возвращает истину.

monk ★★★★★
()
Ответ на: комментарий от monk

Как это противоречит тому, что я написал? Все действия в коде сверху происходят над переменными, а не над литералами.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Все действия в коде сверху происходят над переменными

Именно. И эти переменные имеют разные типы, но при этом одинаковые значения. Что в Haskell невозможно. Int 1 и Integer 1 – это разные значения и их нельзя сравнить или просуммировать.

monk ★★★★★
()
Ответ на: комментарий от Siborgium

Как это противоречит тому, что я написал?

Тем, что целочисленная константа (литерал) сразу имеет целочисленный тип (int c модификаторами), в который помещается. А дальше работает неявное преобразование типов для разных операций: присваивания, сравнения, сложения, вычитания и тп. Из-за неявного преобразования типов язык считается слаботипизипованным и не надо на каждый чих писать конверторы типов int_to_char, int_to_uint, int_to_size_t.

anonymous
()
Ответ на: комментарий от monk

Что в Haskell невозможно. Int 1 и Integer 1 – это разные значения и их нельзя сравнить или просуммировать.

А, так ты к этому. Я приводил литерал как хороший пример того, что не нуждается в типе и описывает само себя. А с переменными в сишке это работает за счет того, что конверсии автоматические, это про репрезентацию числа, а не про само число.

Siborgium ★★★★★
()
Ответ на: комментарий от anonymous

Тем, что целочисленная константа (литерал) сразу имеет целочисленный тип

Этот тип вводится для нее исключительно ради взаимодействия с другими сущностями языка и выбирается на основе значения литерала – а не наоборот.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Этот тип вводится для нее исключительно ради взаимодействия

И этот тип явно определен для этого литерала.

anonymous
()
Ответ на: комментарий от anonymous

И этот тип явно определен для этого литерала.

Я вообще говорю не об этом. Значение переменной определяется ее типом. Тип литерала определяется его значением. Улавливаешь?

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Значение переменной определяется ее типом.

Значение определяется значением, тип - типом. «Значение - типом» - тут я тебя вообще не понимаю.

Тип литерала определяется его значением.

char* p = 0 Какого типа «литерал» 0: int, char, size_t или nullptr_t?

anonymous
()
Ответ на: комментарий от anonymous

Значение определяется значением, тип - типом.

Нет конечно. В переменную типа int32 можно загрузить конечное число значений. Тип переменной вычисляется во время компиляции.

Тип литерала же определяется его значением – ты ввел число, компилятор выбрал тип, который может представить его значение.

Какого типа «литерал» 0:

Согласно таблице будет выбран int. Дальше будут применены правила конверсии между значениями разных типов.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

конечное число значений

По русски это называется «тип» - множество допустимых значений.

Тип переменной вычисляется во время компиляции.

Вывод типа ввели только с++14. В языке Си нет вывода типов.

anonymous
()
Ответ на: комментарий от anonymous

По русски это называется «тип» - множество допустимых значений.

Значение определяется значением, тип - типом. «Значение - типом» - тут я тебя вообще не понимаю.

Не включай дурака.

Вывод типа ввели только с++14. В языке Си нет вывода типов.

Тем проще для твоего понимания. По теме что-нибудь расскажешь?

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Siborgium

Не включай дурака.

Объясни дураку (обсуждение личности), что означает твоё:

Значение переменной определяется ее типом

Вот объявил я переменную опредленного типа void* p;. По твоим словам его значение определено, и какое же это значение?

Ты запутался в своих противоречивых тезисах: то значение опеределяет тип, то тип определяет значение. И перешел на обсуждение личности - назвал меня дураком, поэтому дальнейшее обсуждение не имеет смысла, так началась война без правил

anonymous
()
Ответ на: комментарий от anonymous

Вот объявил я переменную опредленного типа void* p;. По твоим словам его значение определено, и какое же это значение?

Произвольный указатель.

monk ★★★★★
()
Ответ на: комментарий от anonymous

По твоим словам его значение определено, и какое же это значение?

Произвольное значение, представленное в виде sizeof(void*) байт и истолкуемое как указатель. Там может содержаться значение, представимое меньшим числом байтов, но значение, представимое в виде sizeof(void*) + 1 байт туда уже не поместится.

Ты запутался в своих противоречивых тезисах: то значение опеределяет тип, то тип определяет значение.

Не включай дурака (x2). Процитирую себя:

Значение переменной определяется ее типом. Тип литерала определяется его значением.

Противоречия не вижу.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Siborgium

Произвольное значение… истолкуемое как указатель

Рекурсивное определение: значение типа указатель - это значение истолкуемое как указатель. Как еще один твой очень сомнительный тезис.

Не включай дурака (x2).

Обсуждение личности (x2). Слишком высокий уровень обсуждения для меня. За сим откланяюсь.

anonymous
()
Ответ на: комментарий от anonymous

Рекурсивное определение: значение типа указатель - это значение истолкуемое как указатель.

Сам придумал – сам раскритиковал… Когда ты объявляешь таким образом переменную, она инициализируется (условно) произвольными байтами. Поэтому, если ты хочешь говорить о значении, ассоциированном с ней, то это будет произвольное значение из набора значений, представимых таким числом байт. Любое из таких значений в рамках модели языка является валидным указателем, т.е. числом, которое можно использовать для адресации памяти.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Ладно еще пару фраз.

Когда ты объявляешь таким образом переменную

Ты получаешь неопределенное поведение при попытке использовать это самое «определенное значение».

Скоро дойдем до тезисов царя, что неопределенного поведения не существует - все определено, байты уже разложены богом по своим местам, и всё детерминировано.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.