LINUX.ORG.RU

Польза зависимых типов

 ,


1

6
data Format = Number Format
            | Str Format
            | Ch Format
            | Float Format
            | Lit String Format
            | End


PrintfType : Format -> Type
PrintfType (Number fmt) = (i : Int) -> PrintfType fmt
PrintfType (Str fmt) = (str : String) -> PrintfType fmt
PrintfType (Ch fmt) = (char : Char) -> PrintfType fmt
PrintfType (Float fmt) = (float : Double) -> PrintfType fmt
PrintfType (Lit str fmt) = PrintfType fmt
PrintfType End = String


printfFmt : (fmt : Format) -> (acc : String) -> PrintfType fmt
printfFmt (Number fmt) acc = \i => printfFmt fmt (acc ++ show i)
printfFmt (Str fmt) acc = \str => printfFmt fmt (acc ++ str)
printfFmt (Ch fmt) acc = \c => printfFmt fmt (acc ++ show c)
printfFmt (Float fmt) acc = \f => printfFmt fmt (acc ++ show f)
printfFmt (Lit lit fmt) acc = printfFmt fmt (acc ++ lit)
printfFmt End acc = acc


toFormat : (xs : List Char) -> Format
toFormat [] = End
toFormat ('%' :: 'd' :: chars) = Number (toFormat chars)
toFormat ('%' :: 's' :: chars) = Str (toFormat chars)
toFormat ('%' :: 'c' :: chars) = Ch (toFormat chars)
toFormat ('%' :: 'f' :: chars) = Float (toFormat chars)
toFormat ('%' :: chars) = Lit "%" (toFormat chars)
toFormat (c :: chars) = case toFormat chars of
                             Lit lit chars' => Lit (strCons c lit) chars'
                             fmt => Lit (strCons c "") fmt


printf : (fmt : String) -> PrintfType (toFormat (unpack fmt))
printf fmt = printfFmt _ ""
λΠ> :t printf "%c %f %d %s"
printf "%c %f %d %s" : Char -> Double -> Int -> String -> String

λΠ> printf "%c %f %d %s" 'A' 1.234 1 "LOR"
"'A' 1.234 1 LOR" : String

λΠ> printf "%c %f %d %s" 'A' 1.234 1.0 "LOR"
builtin:Type mismatch between
        Double (Type of 1.0)
and
        Int (Expected type)

Еще есть ЯП общего назначения, которые так могут (ну кроме лиспов с макрами, где и то если типы параметров printf известны в compile-time), при этом разработчику не нужно патчить компилятор на каждый чих?



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

C, так может.

printf("%c %f %d %s", 'A', 1.234, 1.0, "LOR");

 warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘double’ [-Wformat=]
fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

нет, си так не может. понятно что для одной конкретной функции можно захардкодить предупреждение в компиляторе, но в примере ТС-а речь не о тупо захардкоженной в компилятор проверке. Ничего про %s, %d в компиляторе нет: это всё написал ТС на коленке за 20 минут.

Это применимо к любым определённым пользователем функциям. Вот например использование этой возможности для работы с сетевыми протоколами http://docs.idris-lang.org/en/latest/st/examples.html

AndreyKl ★★★★★
()

ПФФФФ, частично специфицированый template <char a> для 'd', 'c', 'u' 's' и тд. У меня так лог сделан)

anonymous
()

Что значит «так»? Статический типизированные printf есть в F#, например: https://fsharpforfunandprofit.com/posts/printf/ , для С++ есть https://github.com/fmtlib/fmt , или вот format!/println! в Rust раскрывается в статический типизированный код. Если ли в этом польза? Определено есть. Нужно ли для этого тащить зависимые типы в язык? Получается, что нет.

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

errors in format strings are reported using exceptions

Это плохо?

Да. Весь пойнт зависимых типов - обнаружение ошибок при трансляции.

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

Это плохо?

Это явно даже не то, что в Rust, потому что проверка вообще рантаймовая.

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

Ааа... Я подумал, что притенения чисто к исключениям.

RazrFalcon ★★★★★
()

Еще есть ЯП общего назначения, которые так могут (ну кроме лиспов с макрами, где и то если типы параметров printf известны в compile-time), при этом разработчику не нужно патчить компилятор на каждый чих?

Если не ошибаюсь, Ocaml'овский Printf.printf в статике типы чекает.

open Printf
 
let x = 1
let y = 2
 
let () =
	printf "%d - %s\n" x y

=>

File "prog.ml", line 7, characters 22-23:
Error: This expression has type int but an expression was expected of type
         string
korvin_ ★★★★★
()

при этом разработчику не нужно патчить компилятор на каждый чих?

А у тебя разве компиляция? Я вижу как раз макрос a la lisp, который генерирует строку из данных и в процессе чекает типы. Подобную _функцию_ можно написать в любом языке, а вот возможность применять её на этапе компиляции - это уже отдельная тема.

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

так, на сколько я понимаю, весь смысл зависимых типов в том что есть «возможность применять её (функцию) на этапе компиляции». Т.е. вычислять тип (и правильность типизации) на этапе компиляции в зависимости от значения.

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

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

при этом разработчику не нужно патчить компилятор на каждый чих?

А при чем тут идрис? Его патчить и патчить.

Еще есть ЯП общего назначения, которые так могут

разумеется есть.

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

ну, то что идрис ещё патчить и патчить не отменяет возможность сделать красиво. если есть - примеры кода в студию, чего стесняться. (да, приводить в пример Агду - это чит).

PS. Да, ТС, agda же! там ещё и прикольные символы utf-ом можно писать, лямбды, суммы, что душе угодно ))

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

как раз самая что ни на есть суть вопроса

Не совсем. В языках с динамической типизацией нет разницы, будет ли решение как бы фичей языка, или обычной функцией. Всё равно типы проверяются только в рантайме.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Это упоминание о том что там в языках с «динамической типизацией» с твоей стороны меня честно говоря удивляет: какая разница что там «в языках с динамической типизацией» если ошибка проявит себя в рантайме в итоге? в чём вообще разница между «бросить исключение в рантайме просто проверив параметры» и чем то там о чём ты сейчас упомянул ?

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 1)
Ответ на: комментарий от no-such-file

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

1. В динамически типизированном лиспе можно явно указывать типы.

2. SBCL умеет на основе этого выдавать ошибку еще на этапе компиляции: http://www.sbcl.org/manual/#Getting-Existing-Programs-to-Run

The most common problem is with variables whose constant initial value doesn't match the type declaration. Incorrect constant initial values will always be flagged by a compile-time type error, and they are simple to fix once located.

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

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

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

no-such-file ★★★★★
()

В F# это вроде как работает. Там есть механизм type providers для подобных фишек. Впрочем в DT языках можно провернуть такое когда формат не вычислим в compile time. Например ввести формат строку с клавиатуры, кодом проверить ее на валидность и аккуратно передать затребованные данные, получив при этом полную валидацию от компилятора. Вот только сложно это просто зашибенно.

q0tw4 ★★★★
()
25 марта 2018 г.

С хабра:

Внезапная прорывная новость – комитет предварительно одобрил бумагу о использовании std::allocator в constexpr выражениях. Предстоит ещё огромное количество работы, но оптимистичный прогноз – получить к C++20 std::vector и std::string, которые можно использовать в выражениях на этапе компиляции. Это позволит компиляторам лучше оптимизировать, позволит использовать привычные всем контейнеры для рефлексии, метапрограммирования и метаклассов.

Выглядит интересно. Наконец-то можно будет тайпчекать и разбирать штуки типа printf("%c %f %d %s", 'A', 1.234, 1, "LOR") средствами самого языка, причём на этапе компиляции?

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