LINUX.ORG.RU

[scheme][haskell][oop][fp] Мысли вслух

 , ,


5

7

Была на ЛОРе такая тема — [Haskell] простой вопрос. Хотелось бы немножко её развить и высказаться на тему предпочтения того или иного языка при изучении ФП (графомания mode on :)).

У Scheme есть довольно давняя история использования в качестве подопытного языка в курсах изучения ФП. Я не знаю чем это вызвано, но факт остаётся фактом — есть известный курс у MIT (или был?) и разные полезные книжки — SICP, HTDP, PLAI, OOPLAI, которые обычно и советуют читать если нужно ознакомиться с ФП.

Касательно SICP — одним из сторонников использования ML для целей изучения ФП была написана статья (http://www.cs.kent.ac.uk/people/staff/dat/miranda/wadler87.pdf) в которой достаточно хорошо освещены некоторые недостатки Scheme. Если говорить про Haskell, то тут всё так же. Далее, по пунктам (опуская кое-что из того что уже было в той статье).

Более явный синтаксис

Вместо

(define (foo x y z)
  (if (> (+ x (* y z) 1) 7) (print (+ x y)) (print (- x y))))

будет

foo x y z = if x + y * z + 1 > 7 then print $ x + y else print $ x - y

при этом по-прежнему можно написать выражение в префиксной форме:

(if' ((>) ((+) x ((*) y z) 1) 7) (print ((+) x y)) (print ((-) x y)))

почти как в Scheme. То есть, кроме префикса также есть (расширяемый пользователем) инфикс (в том числе функции вроде ($) и (.) позволяющие в некоторых случаях опускать лишние аргументы у функций и некоторые скобки в выражениях) и разные специальные формы (вроде if, let, лямбды и т.п.). Во всём что не касается макросов это более удобно. S-выражения обретают свой особый смысл только когда доходит до их цитирования:

'(if (> (+ x (* y z) 1) 7) (print (+ x y)) (print (- x y)))

и разбора с целью написания макросов. Тем не менее, для изучения именно ФП эта возможность незначительна (ФП не про макросы, в SICP и HTDP не ни слова про макросы, в PLAI есть только немного, в OOPLAI — побольше). Про то как правильно (ну, _правильно_, то есть без использования s-выражений) организовывать символьные вычисления (вроде дифференцирования из SICP) также расказывается в упомянутой статье.

Каррированные функции

Такое определение, например:

(define add
  (lambda (n)
    (lambda (m)
      (+ m n))))

заменяется простым

add = (+)

так как все функции уже каррированные (позволяют частичное применение). Фактически, в хаскеле функция с n аргументами сразу задаёт n разных функций (выбор конкретной функции осуществляется во время компиляции и не имеет эффекта во время выполнения). Некаррированные функции это функции от кортежей (те и другие переводятся друг в друга с помощью ФВП carry/uncarry).

Частичное применение, секции, pointfree запись

add2 = (+ 2)

add2 5
7

вместо

(define add2 (add 2))

(add2 5)
7

Мутабельные замыкания

Это единственная вещь которая есть в Scheme и которую можно не увидеть сразу в хаскеле (и про неё нет в той статье). Тот тред был как раз про них. Чтобы прояснить этот момент, ниже приводятся некоторые примеры из OOPLAI и их аналоги на хаскеле.

Простейший вариант:

(define counter
  (let ((count 0))
    (lambda ()
      (begin
        (set! count (add1 count))
        count))))

(counter)
1
(counter)
2

аналог:

counter = (=~ (+ 1)) <$> new 0

тут (=~ (+ 1)) играет роль мутирующего «метода», а (new 0) — мутируемого «объекта», (<$>) — применение «диспетчера» (тут — просто единичный анонимный «метод»). Вся конструкция функториальная (не монадическая). Использование:

ctr <- counter      -- Инстанцирование класса counter объектом ctr.
ctr                 -- Применение единственного метода ((=~ (+ 1)) который).
1                   -- Результат.
ctr                 -- Снова.
2                   -- Другой результат.

Чуть более сложный пример:

(define counter-
  (let ((count 0))
    (lambda (cmd)
      (case cmd
        ((dec) (begin
                 (set! count (sub1 count))
                 count))
        ((inc) (begin
                 (set! count (add1 count))
                 count))))))

(counter- 'inc)
1
(counter- 'dec)
0

Для начала определим имена методов dec и inc:

data CounterMethod = Dec | Inc

это не символы и не строки (так что код не будет ill-typed как в примере на Scheme, иначе говоря, применение несуществующего метода не пройдёт компиляции). И теперь функцию:

counter' = dispatch <$> new 0
  where dispatch obj Dec = obj =~ flip (-) 1
        dispatch obj Inc = obj =~ (+ 1)

тут dispatch играет роль диспетчеризирующей функции которая получает объект (obj) и имя метода, а затем изменяет объект (как того требует метод). (new 0) — начальный объект.

Пример:

ctr <- counter'     -- Инстанцирование класса counter' объектом ctr.
ctr Inc             -- Применение метода Inc на объекте ctr.
1
ctr Inc
2
ctr Inc
3
ctr Dec             -- Тут уже метод Dec.
2
ctr Dec
1
ctr Dec
0

Тут применение (ctr Inc) весьма похоже на каноничное, через точку, obj.method и является, по сути, посылкой сообщения объекту.

Третий пример:

(define stack
  (let ((vals '()))
    (define (pop)
      (if (empty? vals)
          (error "cannot pop from an empty stack")
        (let ((val (car vals)))
          (set! vals (cdr vals))
          val)))
    (define (push val)
      (set! vals (cons val vals)))
    (define (peek)
      (if (empty? vals)
          (error "cannot peek from an empty stack")
        (car vals)))
    (lambda (cmd . args)
       (case cmd
         ((pop) (pop))
         ((push) (push (car args)))
         ((peek) (peek))
         (else (error "invalid command")))))) 

(stack 'push 1)
(stack 'push 2)
(stack 'pop)
2
(stack 'peek)
1
(stack 'pop)
1
(stack 'pop)
; cannot pop from an empty stack

аналогично:

data StackMethod = Pop | Push | Peek

stack = dispatch <$> new []
  where
    dispatch x Pop _  = get x >>= (x =~ tail >>) . return . head
    dispatch x Push y = x =~ (y :) >> return y
    dispatch x Peek _ = head <$> get x

и пример:

stk <- stack :: IO (StackMethod -> Int -> IO Int)
                    -- stack это параметрически-полиморфный класс, в данном
                    -- случае берётся его спецификация когда элементы стека
                    -- имеют тип Int (можно взять что-то более общее).
                    -- Этот специфичный класс инстанцируется объектом stk.
mapM_ (stk Push) [1, 2, 3]
                    -- (stk Push) это применение метода Push на объекте stk,
                    -- с помощью ФВП mapM_ оно производится для всех элементов
                    -- списка.
repeat 4 $ stk Pop __ >>= print
                    -- 4 раза вызывается метод Pop, элементы печатаются.
                    -- Последний раз вызывается исключение (так как стек пуст).
3
2
1
*** Exception: Prelude.head: empty list

тут точно так же — в StackMethod перечислены нужные методы для стека, функция stack определяет класс, то есть объединение данных и функций с нужным поведением, она имеет тип IO (StackMethod -> a -> IO a), то есть принимает метод, элемент стека и возвращает элемент стека (в IO, мутабельно), сама функция в IO (вся структура данных ведёт себя мутабельно).

Дальше в OOPLAI начинают использовать макросы для придания более удобоваримого вида этим конструкциям. В настоящем (ну, _настоящем_ :)) ФП этого не нужно — примитивные ООП конструкции объединяющие данные и функции выглядят естественно и так, и являются частным случаем использования ФВП, IO и ADT с паттерн-матчингом (последние два — для удобства). Использование макро-системы может иметь смысл разве что если действительно нужно реализовать сложную ООП систему (например, со множественным наследованием и изменяемой иерархией классов, впрочем, обойтись одними функциями тут тоже можно, просто придётся делать больше механических действий).

Ещё пример:

-- | Данные — конструктор и аккессоры.
data Point = Point
  { x :: Double
  , y :: Double
  } deriving ( Show, Eq ) -- ad-hoc перегруженные функции.

-- | Методы привязываемые к данным (это уже _не_ ad-hoc перегруженные функции).
data PointMethod = Pos | Mov

-- | Класс (= функция), объединяющий данные и методы.
pointClass :: Double -> Double -> IO (PointMethod -> Double -> Double -> IO Point)
pointClass initX initY = dispatch <$> new (Point initX initY)
  where
    -- | (Динамический) диспетчер по методам. Он принимает объект (Var Point),
    -- имя метода (PointMethod, т.е. статическое, в данном случае, сообщение)
    -- и два опционных аргумента для методов (Double -> Double). Эту функцию
    -- можно помещать непосредственно в Point.
    dispatch :: Var Point -> PointMethod -> Double -> Double -> IO Point
    dispatch obj Pos _ _ = get obj
    dispatch obj Mov x y = obj =: Point x y
pnt <- pointClass 2 4         -- Инстанцирование класса pointClass объектом pnt
                              -- с начальными значениями полей 2 и 4.
:t pnt
pnt :: PointMethod -> Double -> Double -> IO Point
pnt Pos __ __                 -- Вызов метода Pos на объекте pnt.
Point {x = 2.0, y = 4.0}
pnt Mov 3 5                   -- Вызов метода Mov.
Point {x = 3.0, y = 5.0}
pnt Pos __ __                 -- Положение изменилось:
{x = 3.0, y = 5.0}

Нужно заметить, что это всё довольно примитивные конструкции (простые функции и IO). В случае использования ADT для имён методов получится динамическая диспетчеризация с фиксированным набором методов (well-typed), если же переписать функцию dispatch с завязкой на хэш-табличку (которая должна быть переменной в данных класса), то будет динамическая диспетчеризация с пополняемым набором методов и перегруженными методами (одни и те же сообщения можно посылать разным инстанцированным объектам, их dispatch будет их искать в хэш-таблице и обрабатывать, это уже ill-typed, то есть с исключениями вида «нет такого метода»). Разные прочие вещи вроде наследования и self точно также можно изобразить (аггрегация данных, представление иерархии классов в данных (в переменной или нет, в зависимости от возможности менять иерархию) и более сложная функция dispatch), но как-то не интересно.

P.S.

Код на хаскеле использует такие упрощения:

import Prelude hiding ( repeat )
import Data.IORef
import Control.Applicative
import Control.Monad

type Var a = IORef a

new :: a -> IO (IORef a)
new = newIORef

get :: IORef a -> IO a
get = readIORef

(=~) :: IORef a -> (a -> a) -> IO a
x =~ f = modifyIORef x f >> get x

(=:) :: IORef a -> a -> IO a
x =: x' = x =~ const x'

repeat :: Monad m => Int -> m a -> m ()
repeat = replicateM_

__ :: a
__ = undefined

P.P.S.

OOP / ООП в контексте данного поста — объектно-ориентированное программирование в духе объединения данных и процедур, то есть в духе C++, Java, Python и т.п. _Не_ ООП в духе классы = структуры, методы = перегруженные функции, наследование = схемы агрегаций и распространения методов (как это в CLOS и классах типов Haskell).

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

Неправильно! Надо (id :: a -> a).

Ну пусть будет так (можно просто new id, без типов).

Ты сам понимаешь - на практике это в подобном виде неюзабельно.

А я не знаю что нужно. Поместить чистую функцию f в переменную и дальше определить функцию h через f - как? Либо как чистую же функцию с однозначным захватом f, либо как _действие_ которое достанет f из переменной и вызовет её. В первом случае будет не как в scheme (а как в ghci по умолчанию), во втором случае - будет как в scheme. На практике, если нужна storable функция в переменной, можно сделать нормальные типы данных, умные конструкторы написать, ну и вообще сделать чтобы было юзабельно.

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

На практике, если нужна storable функция в переменной

На практике все ф-и должны быть storable-переменными.

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

Ну пусть будет так (можно просто new id, без типов).

И что тогда будет с типизацией (когда мы попытаемся сделать ее (+1))?

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

На практике все ф-и должны быть storable-переменными.

А если я так не хочу, а хочу, чтобы функции были чистыми?

И что тогда будет с типизацией (когда мы попытаемся сделать ее (+1))?

TI выведет id :: Integer -> Integer, f :: IORef (Integer -> Integer), ...

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

Вообще без этой фичи (возможность переопределить уже существующие ф-и) репл-то и не нужен.

Репл в хаскеле это такая штука, в которую можно грузить разные модули/файлы и каждый раз (между такими загрузками) давать любые определения возможные в hs файле, т.е. проводить какие-то эксперименты, проверять что получилось и т.п. Часто бывает, что результаты этих экспериментов и проб выносятся и переписываются в нормальный статический тест. Т.е. это вспомогательное средство. Тут надо разделять понятия REPL и hot patching. А чтобы переопределить уже существующие ф-и достаточно сделать C-c C-l в буфере.

Кстати:

$ c-repl
> int f(int x) { return x; };
> int g(int(*f_)(int), int x) { return f_(f_(x)); };
> int h(int x) { return g(&f, x); }
> printf("%i\n", h(10));
10
> int f(int x) { return x + 1; };
> printf("%i\n", h(10));
10
> 
quasimoto ★★★★
() автор топика
Ответ на: комментарий от quasimoto

c-repl

Он, оказывается, просто не умеет переопределять функции.

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

то есть ты не можешь опистаь, как работает ф-я? Ок.

Могу, но не вижу надобности: Y-комбинатор проще, при этом его тоже нет.

А через Y-комбинатор функция loop пишется элементарно:

(define (loop f)
  (lambda (a)
    (Y (lambda (bz)
         (f (cons a (cdr bz)))))))

Ну тогда надо будет запретить, например, применять функторы к грязным ф-ям и т.п.. нахрена тогда будут нужны такие функторы?

Вообще-то энергичность и нечистота — тоже разные вещи.

Нет падает, если сделать соответствующие синтаксические преобразования (доставить скобки).

Запусти. Я это сделал. Оно упало.

Дай свое название, хз.

Конструктор типа.

Это именно порядок. Какое вычисление за каким следует.

Нет. Не «какое вычисление», а «какая операция ввода-вывода».

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

«пусть не врубаются в материал и плюются дальше»?

Нет. Тут уже поминали хорошую книгу «Хаскель через мультимедиа», вот так и надо.

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

Вообще без этой фичи (возможность переопределить уже существующие ф-и) репл-то и не нужен.

Переопределить — пожалуйста, но зачем при этом ломать другие, внешне никак не связанные с этой, функции?

И вообще, репл работает так же, как работал бы файл.

Мне как-то плевать, откуда происходит снижение вероятности, важно только, какова эта вероятность.

Безусловно. И это связано с происхождением.

Сколько, сколько.

Нет. Ты же уже признался, что не знаешь хаскеля.

А что тогда?

Ну, поначалу — с какими шансами функции из SICP-а грохнутся.

Линк?

[scheme][haskell][oop][fp] Мысли вслух (комментарий)

Нет, я про хаскель. Там тоже делают.

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

А прблема плюсов совсем не типизация.

Да, проблема плюсов — плюсы.

О чем я и говорю.

А статика — поймает. Без «может».

Ну так пусть пишут с зависимыми типами, наверное?

С зависимыми типами пока непонятно. У меня пока впечатление, что это тупиковый путь. Лучше подождать, во что это выльется.

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

Ну да, в haskell-98 она ограничена rank-1

Да.

есть: (\x -> ...) это она.

Нет. Это маленькая лямбда.

Для любой LAMBDA можно написать \x -> ... которая будет эквивалентна.

Нельзя.

Нет, полные и везде определенные.

Это неверно. Скажем, тот же Y-комбинатор в Хаскеле пишется, в System F — нет. Следовательно, H98 в F не вкладывается. Наоборот, полиморфизм более высокого ранга не вкладывается в H98.

Тогда «маленький кусок» - тоже неприменимо.

Понятно, то есть, вторая часть возражений не вызвала?

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

Многозначные — да. Модуль в анализе и модуль в алгебре — принципиально разные вещи. А вот System F — понятие однозначное.

И, кстати, завязка на контекст и «практически» — сугубо разные вещи.

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

Не первый раз уже вижу, как ты с Митяем дискутируешь. Как только у тебя терпения на это хватает?

У него ведь и аргументация всякий раз одинаковая. Следите за руками:
Никакого «Есть» и «Нет» не бывает. Потому, что ошибки компиляторов, космические лучи, гигантская флуктуация.
Бывает только «Бывает» и «Не бывает». С какой-то вероятностью.
Вероятность бывает только 50% (или встретит или не встретит).
Следовательно, между «Бывает» и «Не бывает» нет разницы.
Получается, если в хаскеле есть статическая проверка, то на самом деле это означает, что проверка «бывает».
А это то же самое, что и «не бывает».
Кроме того:
ЖОПА - это Top. И ПАЛЕЦ - тоже Top.
Значит нет разницы между ЖОПА и ПАЛЕЦ. Разницы вообще нет.
Следовательно, нет разницы между Haskell и System F, типами-суммами и неразмеченными объединениями (это он мне на полном серьезе доказывал) и т.д.
Вывод: лисп луччей!

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

Есть, но он не false.

Лучше сказать, что он имеет смысл в контексте списков, а не перегружен как в лиспе (недо-Nothing).

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

Т.е., «пусть не врубаются в материал и плюются дальше»?

Пусть поменяют на что-нибудь более адекватное (в рамках обучения), Standart ML, например.

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

haskell вполне себе подходит для обучения

x4DA ★★★★★
()

тут (=~ (+ 1)) играет роль мутирующего «метода», а (new 0) — мутируемого «объекта», (<$>) — применение «диспетчера» (тут — просто единичный анонимный «метод»). Вся конструкция функториальная (не монадическая). Использование:
(использует do-нотацию)

/facepalm
А за использование ООП-сленга не к месту я бы вообще убивал.

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

А если я так не хочу, а хочу, чтобы функции были чистыми?

Ну они и должны быть чистыми, да. Кто ж вам виноват что нельзя переопределять чистые? В том и отличие полноценного репла от костыльного.

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

TI выведет id :: Integer -> Integer, f :: IORef (Integer -> Integer), ...

А если потом окажется, что там должно было быть String -> String?

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

Ну так суть репла именно в том, чтобы можно было экспериментировать. То есть - хотим поправить код, меняем некий кусок (причем заведомо известно что код стал некорректен). Проверяем работу этого куска. Плохо - исправляем - плохо - исправляем - плохо - исправляем - хорошо - фиксим остаток программы. В хаскеле придется: фиксим остаток программы - плохо - исправляем - фиксим остаток программы - плохо - исправляем...

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

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

Могу, но не вижу надобности

Ну не видишь так не видишь.

Y-комбинатор проще, при этом его тоже нет.

Как нет, если я его написал?

Вообще-то энергичность и нечистота — тоже разные вещи.

Ну да.

Запусти. Я это сделал. Оно упало.

Почему у меня не падает (тот код что я привел)?

Конструктор типа.

Так речь идет не о нем, речь идет о сущности, которую этот конструктор конструирует. List - тоже конструктор типа, но мы когда говорим о списках обычно подразумеваем вполне конкретную структуру данных, а не конструктор. Конструктора может вообще не быть (например ЯП без АТД) а списки там будут. Аналогично и с IO - у нас может быть энергичный ЯП, в котором отсутствуют монады, тайпклассы и АТД (и вообще типы), а IO (как инкапсулирующая последовательность вычислений сущность) будет. При чем от представления этой сущности мы абстрагированы - стандарт хаскеля не запрещает представлять IO в виде кода на питоне, например.

Нет. Не «какое вычисление», а «какая операция ввода-вывода».

Нет, именно вычисление, потому что это то, что находится внутри IO, то есть «вывести единицу» - вычисление, например. «Применить ф-ю f к аргументу x» - тоже вычисление (если подняли его в IO). Но уже не операция ввода-вывода.

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

Переопределить — пожалуйста, но зачем при этом ломать другие, внешне никак не связанные с этой, функции?

А мы их не ломаем, мы просто экспериментируем с куском программы, для которого корректность этих не связанных (на то и не связанные) ф-й не важна. В хаскеле репл это делать не позволяет - надо сперва исправить все «несвязанные» ф-и.

Нет. Ты же уже признался, что не знаешь хаскеля.

Где?

Ну, поначалу — с какими шансами функции из SICP-а грохнутся.

Около нуля. Что дальше?

[scheme][haskell][oop][fp] Мысли вслух (комментарий)

Ну мы уже выяснили, что этот пример грохнется во время отладки, точно тогда же и так же, когда грохнется в случае статики. Что ты пытаешься доказать?

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

Язык, в котором даже списков полноценных сделать нельзя, полноценным не может быть априори.

Да, проблема плюсов — плюсы.

Да. Так при чем тут плюсы?

А статика — поймает. Без «может».

Мы уже выяснили - и статика и динамика «может» ловят, только с разной вероятностью. Надо еще обратить внимание на один факт - статика ловит ошибку только тогда, когда ты покрыл эту ошибку типами. А ведь ты мог и не покрыть. Или аналогично покрыть ее тестами в динамике.

С зависимыми типами пока непонятно. У меня пока впечатление, что это тупиковый путь. Лучше подождать, во что это выльется.

Ну ты же сам сказал - нечего к гадостям приучить. вот и пусть пишут максимально корректно сразу, почему нет?

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

Нет. Это маленькая лямбда.

Маленькая лямбда - это всегда мономорфная лямбда. А (\x -> ...) - это полиморфная лямбда, нельзя никакой маленькой лямбды написать, которая будет эквивалентна (\x -> ...).

Нельзя.

Пример? (в рамках rank-1 конечно же).

Наоборот, полиморфизм более высокого ранга не вкладывается в H98.

при чем тут полиморфизм более высокого ранга, если мы о SystemF с rank-1?

Понятно, то есть, вторая часть возражений не вызвала?

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

Многозначные — да. Модуль в анализе и модуль в алгебре — принципиально разные вещи.

А есть и менее значительные. Ну например - в рамках какой конкретно из теорий множеств ведется изложение, и, соответственно, что конкретно означает «множество».

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

типами-суммами и неразмеченными объединениями (это он мне на полном серьезе доказывал) и т.д.

У тебя склероз, братюнь.

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

Как нет, если я его написал?

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

Почему у меня не падает (тот код что я привел)?

Потому что ты его не запускаешь, видимо. Запусти и приведи лог.

то есть «вывести единицу» - вычисление, например

Ну, тогда порядок таких вычислений отлично специфицируется.

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

А мы их не ломаем

Как же не ломаем? Есть функция, она как-то работает. Мы взяли и поменяли нечто, на вид с ней не связанное. Она вдруг стала работать иначе. WTF?

Где?

[scheme][haskell][oop][fp] Мысли вслух (комментарий)

Тебе не очевидно, что делает короткий и ясный кусок кода.

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

Во-первых, не выяснили. Мы выяснили, что он МОЖЕТ грохнуться при отладке.

Во-вторых, ты решил подтвердить мою точку зрения? Что в SICP падучий код?

Язык, в котором даже списков полноценных сделать нельзя

Полноценные списки - это списки, не требующие кастов, и ясно говорящие, что именно они содержат и что с этим можно делать. Кроме Хаскеля и ML-языков их нигде нет.

Так при чем тут плюсы?

Не знаю, ты их зачем-то вспомнил.

только с разной вероятностью

Ну и прекрасно. Рад, что ты наконец согласился.

А ведь ты мог и не покрыть

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

вот и пусть пишут максимально корректно сразу, почему нет?

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

А (\x -> ...) - это полиморфная лямбда, нельзя никакой маленькой лямбды написать, которая будет эквивалентна (\x -> ...).

Нельзя - в смысле, в System F? Да, нельзя.

А теперь возвращаемся к нашим баранам. Как ты собираешься писать полиморфными лямбдами типы высших рангов из System F?

если мы о SystemF с rank-1?

С какого хуя мы вдруг стали о System F с rank-1? Она слишком ограниченная, соответственно, не нужна никому нахрен.

Ну например - в рамках какой конкретно из теорий множеств ведется изложение

Ты в каком веке живёшь? Сейчас теория множеств осталась одна.

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

Ты написал какую-то хрень, которая не работает.

Работает.

Потому что ты его не запускаешь, видимо. Запусти и приведи лог.

Он запускается и виснет, все как надо. Приведи лог, где падает.

Ну, тогда порядок таких вычислений отлично специфицируется.

В хаскеле? Нет. Только в IO, которое уже не хаскель.

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

Как же не ломаем? Есть функция, она как-то работает. Мы взяли и поменяли нечто, на вид с ней не связанное. Она вдруг стала работать иначе. WTF?

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

Тебе не очевидно, что делает короткий и ясный кусок кода.

Как и тебе.

Во-первых, не выяснили. Мы выяснили, что он МОЖЕТ грохнуться при отладке.

Именно так.

Во-вторых, ты решил подтвердить мою точку зрения? Что в SICP падучий код?

Нет, твоя точка зрения была в том, что он стал бы менее падучим со статической типизацией. И я уже доказал фактами, что эта точка зрения не верна. И со статической и с динамической типизацией код одинаково падуч (то есть он упадет во время отладки на одинаковых тестах). Какова именно эта падучесть (и вообще отлична ли она от нуля) в данном случае обсуждать смысла нет.

Полноценные списки - это списки, не требующие кастов, и ясно говорящие, что именно они содержат и что с этим можно делать.

Прекрасно, я хочу список, который содержит любые элементы. Сделай мне такой список.

Не знаю, ты их зачем-то вспомнил.

Их ты вспомнил.

Ну и прекрасно. Рад, что ты наконец согласился.

Я с этим и не спорил.

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

С чего вдруг?

В каких-то - лучше.

Это в каких?

В среднем - посчитай сам.

Ты еще забыл пр ослучаи, в которых они хуже.

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

Но корректнее, чем в каком-нибудь хаскеле, так?

Нельзя - в смысле, в System F? Да, нельзя.

Да нигде нельзя. И в хаскеле в том числе. Мономорфной лямбдой полиморфную не опишешь никак.

А теперь возвращаемся к нашим баранам. Как ты собираешься писать полиморфными лямбдами типы высших рангов из System F?

Не понял вопроса. Полиморфные лямбды - это _и есть_ типы высших рангов из System F. То есть - берешь и пишешь. Это во-превых, во-вторых - а зачем их писать, если у нас только rank-1?

С какого хуя мы вдруг стали о System F с rank-1?

Потому что мы говорим о ней в контексте хаскеля. Мы выяснили это уже страницы три назад, а ты до сих тормозишь.

Она слишком ограниченная, соответственно, не нужна никому нахрен.

А все эти системы типов никому в чистом виде не нужны, обычно они используются с какими-то расширениями. Вот тот же хаскель-98 - System F с расширениями.

Ты в каком веке живёшь? Сейчас теория множеств осталась одна.

Ну в литературе в зависимости от ситуации в равной мере на данный момент используются три: наивная, ZF, NBG.

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

Он запускается и виснет, все как надо.

Какой же это Y-комбинатор, если он на такой фигне виснет?

В хаскеле? Нет. Только в IO, которое уже не хаскель.

А в хаскеле и не надо, ибо на результат не влияют.

Так нам не важно, что она стала работать иначе.

Если нам не важно - то в чём претензии?

Как и тебе.

Пруфлинк?

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

Да.

И я уже доказал фактами, что эта точка зрения не верна. И со статической и с динамической типизацией код одинаково падуч (то есть он упадет во время отладки на одинаковых тестах).

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

Прекрасно, я хочу список, который содержит любые элементы. Сделай мне такой список.

[forall a. a]

Сделал. Да, это не H98, но это давно устоявшееся расширение.

Хочешь без расширений? Пожалуйста:

[()]

Поскольку с «любыми элементами» ты не можешь сделать ничего вообще, списка [()] будет вполне достаточно.

Их ты вспомнил.

Нет. Ты стал вспоминать о том, как жертвуют выразительностью. Это явно про C++.

С чего вдруг?

Ну, ты отличий-то не приводишь.

Ты еще забыл пр ослучаи, в которых они хуже.

Дык пока не видно.

Да нигде нельзя.

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

Но корректнее, чем в каком-нибудь хаскеле, так?

Что значит «корректнее»?

Полиморфные лямбды - это _и есть_ типы высших рангов из System F.

Нет, конечно. Это всего лишь rank-1.

Как я уже говорил, если убрать из H98 рекурсивные типы, то, что останется, вкладывается в System F с рангом не выше 1. При этом, полиморфные лямбды остаются. Ergo, полиморфные лямбды - это ранг 1.

Потому что мы говорим о ней в контексте хаскеля. Мы выяснили это уже страницы три назад, а ты до сих тормозишь.

Мы выяснили три страницы назад, что математические понятия имеют строгие определения.

А все эти системы типов никому в чистом виде не нужны

System F в чистом виде нужна. Теоретикам. System F, ограниченная рангом 1, не нужна никому, она недостаточно выразительна.

Вот тот же хаскель-98 - System F с расширениями.

Нет, это маленькое подмножество System F (а именно, то, что имеет ранг 1) плюс очень солидное расширение (рекурсивные типы).

Ну в литературе в зависимости от ситуации в равной мере на данный момент используются три

Наивная - только в научпопе, NBG - только в работах по основаниям, причём это специально оговаривается.

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

«Хаскель через мультимедиа»

Спасибо.

(Подсказка любопытным: google thepiratebay haskell).

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

Какой же это Y-комбинатор, если он на такой фигне виснет?

А почему он не должен на ней виснуть?

А в хаскеле и не надо, ибо на результат не влияют.

Влияют, мы это уже выяснили.

Если нам не важно - то в чём претензии?

Претензии в том, что из-за статической типизации нам надо исправлять этот остальной код, хотя он нам и не важен.

Пруфлинк?

Ну ты же не смог объяснить, что она делает.

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

«Не компилится» = «падает во время отладки». И в динамическом и в статическом случае он упадет во время отладки. В чем проблема?

[forall a. a]
Сделал. Да, это не H98, но это давно устоявшееся расширение.

Смеешься? Это устоявшееся расширение вообще-то deprecated. Но даже так:

$ ghci -XImpredicativeTypes

Prelude> [1,"2"] :: [forall a.a]

<interactive>:1:1:
    Could not deduce (Num a) from the context ()
      arising from the literal `1' at <interactive>:1:1
    Possible fix:
      add (Num a) to the context of the polymorphic type `forall a. a'
    In the expression: 1
    In the expression: [1, "2"] :: [forall a. a]
    In the definition of `it': it = [1, "2"] :: [forall a. a]

<interactive>:1:3:
    Couldn't match expected type `a' against inferred type `[Char]'
      `a' is a rigid type variable bound by
          the polymorphic type `forall a. a' at <interactive>:1:0
    In the expression: "2"
    In the expression: [1, "2"] :: [forall a. a]
    In the definition of `it': it = [1, "2"] :: [forall a. a]
Prelude>

Поскольку с «любыми элементами» ты не можешь сделать ничего вообще, списка [()] будет вполне достаточно.

Принимается, это разумный аргумент.

Понятно, что «списки чего угодно» нам на практике не нужны. На практике нам могут понадобиться списки элементов, реализующих определенный интерфейс (принадлежащих определенному тайпклассу). Напиши, пожалуйста, map для таких списков.

Нет. Ты стал вспоминать о том, как жертвуют выразительностью. Это явно про C++.

Нет, это про хаскель.

Ну, ты отличий-то не приводишь.

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

Дык пока не видно.

Все примеры из оп-поста же.

Я просто уточняю, что именно ты пишешь.

Еще раз - выразить полиморфную функцию мономорфной лямбдой нельзя ни в какой системе.

Что значит «корректнее»?

Значит «гарантируется отсутствие более широкого класса ошибок». Ведь нам именно это нужно?

Нет, конечно. Это всего лишь rank-1.

Высшие ранги тоже определяются полиморфными лямбдами. Иначе бы в хаскеле не было rank-n.

Как я уже говорил, если убрать из H98 рекурсивные типы, то, что останется, вкладывается в System F с рангом не выше 1.

То есть haskell-98 - это rank-1 System F с расширениями, о чем я с самого начала и сказал. И с чем ты начал спорить.

Ergo, полиморфные лямбды - это ранг 1.

Полиморфные лямбды - это rank-n, чтобы получить rank-1 надо эти полиморфные лямбды ограничить (как они ограничены в haskell-98 например).

Мы выяснили три страницы назад, что математические понятия имеют строгие определения.

Конечно, строгие, но разные в зависимости от того, в каком контексте используется понятия.

System F в чистом виде нужна. Теоретикам. System F, ограниченная рангом 1, не нужна никому, она недостаточно выразительна.

Она равно нужна и с ограничениями и без.

Нет, это маленькое подмножество System F

Нет, это почти полное подмножество System F. С определенными расширениями, да.

Наивная - только в научпопе, NBG - только в работах по основаниям, причём это специально оговаривается.

Да нет, как NBG часто встречается во вполне обычных (не связанных с основаниями) работах (когда нам нужны классы), так и наивная так же часто встречается в работах, где теория множеств не важна вовсе.

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

А почему он не должен на ней виснуть?

Ну, хаскельный не виснет. Находит неподвижную точку. А этот - не находит.

Влияют, мы это уже выяснили.

Лжёшь.

Претензии в том, что из-за статической типизации нам надо исправлять этот остальной код, хотя он нам и не важен.

Зачем исправлять, если он не важен?

Ну ты же не смог объяснить, что она делает.

Смог, даже переписал для тебя на схеме, только через Y (которого нет).

«Не компилится» = «падает во время отладки».

Нет, нечему падать.

Смеешься? Это устоявшееся расширение вообще-то deprecated.

Ну да, да, подловил. Написал для краткости. Правильно так:

data Ubiq where Ubiq :: a -> Ubiq
type AllList = [Ubiq]

На практике нам могут понадобиться списки элементов, реализующих определенный интерфейс (принадлежащих определенному тайпклассу).

Верно. То есть, список таких штук:

data Showable where Showable :: Show a => a -> Showable

Напиши, пожалуйста, map для таких списков.

Чем Prelude.map не нравится?

Нет, это про хаскель.

Не, это явно про плюсы. Может, ты хоть их знаешь?

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

Не тебе судить. Если ты, конечно, не вспомнил опять про плюсы.

Все примеры из оп-поста же.

Из чего?

Значит «гарантируется отсутствие более широкого класса ошибок». Ведь нам именно это нужно?

Нам нужно получить на выходе хорошую программу.

Высшие ранги тоже определяются полиморфными лямбдами. Иначе бы в хаскеле не было rank-n.

В хаскеле и нет rank-n. В Haskell98, по крайней мере.

А если ты про расширения, то там всё не лямбдами делается, а forall-ами (которые суть хаскельные аналоги «большой лямбды» из System F).

То есть haskell-98 - это rank-1 System F с расширениями, о чем я с самого начала и сказал.

Врёшь. Ты не сказал этого.

Конечно, строгие, но разные в зависимости от того, в каком контексте используется понятия.

Понятия - нет. Понятия не имеют разных определений. Слова - да, те понятия, которые они обозначают, зависят от контекста. Но «System F» - не многозначное слово(-сочетание).

Она равно нужна и с ограничениями и без.

Ну-ну. В теории ты тоже ноль.

Нет, это почти полное подмножество System F.

System F подразумевает ПРОИЗВОЛЬНЫЕ ранги.

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

Ну, хаскельный не виснет. Находит неподвижную точку. А этот - не находит.

Да?

Prelude> let fix f = f $ fix f
Prelude> let g x = if x then x else (g x)
Prelude> fix g

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

Лжёшь.

Ну как нету? если я выведу на экран сперва «1», а потом «2», то это не то же самое, что вывести наоборот. Значит - влияют.

Зачем исправлять, если он не важен?

Ну вот именно, зачем исправлять, если он не важен? А статическая типизация заставляет исправлять. Иначе ничего не запускается, пока не исправлю :(

Смог, даже переписал для тебя на схеме, только через Y (которого нет).

То есть ты написал какой-то нерабочий код на схеме, который ничего не делает. Странное объяснение.

Нет, нечему падать.

А чего ж оно падает, если нечему? Вот начинаю я отладку, жму «тайчпек» и тут о-па! Все, падает с сообщением об ошибке вместо того, чтобы заработать.

Верно. То есть, список таких штук:

А теперь напиши для них map.

Чем Prelude.map не нравится?

Не работает:

Prelude> map show [1, "2"]

<interactive>:1:10:
    No instance for (Num [Char])
      arising from the literal `1' at <interactive>:1:10
    Possible fix: add an instance declaration for (Num [Char])
    In the expression: 1
    In the second argument of `map', namely `[1, "2"]'
    In the expression: map show [1, "2"]
Prelude>

Не, это явно про плюсы. Может, ты хоть их знаешь?

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

Не тебе судить.

Конечно, не мне. Я могу лишь делать субъективные выводы. Ну например - ЯП в котором нельзя реализовать функцию map не может быть выразительным. Это субъективный вывод. В хаскеле функцию map реализовать нельзя - это уже объективный факт. Получаем следствие - хаскель невыразительный ЯП. Если для кого-то невозможность написания такой тривиальной вещи как map не является признаком невыразительности - что ж, для этого человека хаскель вполне выразителен.

Из чего?

Из первого поста этого треда.

Нам нужно получить на выходе хорошую программу.

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

В хаскеле и нет rank-n. В Haskell98, по крайней мере.

Верно, нет. Я и не говорил, что есть.

А если ты про расширения, то там всё не лямбдами делается, а forall-ами (которые суть хаскельные аналоги «большой лямбды» из System F).

Ну вот, ты же сам сказал - аналог большой (полиморфной) лямбды.

Врёшь. Ты не сказал этого.

С самого начала я лишь опустил rank-1, полагая, что всем все понятно из контекста. Несколько постов спустя я увидел, что непонятно, и это уточнение (про rank-1) сделал. Это было еще хз когда, а ты все продолжаешь цирк.

Понятия - нет. Понятия не имеют разных определений. Слова - да, те понятия, которые они обозначают, зависят от контекста. Но «System F» - не многозначное слово(-сочетание).

Любое слово - многозначное.

System F подразумевает ПРОИЗВОЛЬНЫЕ ранги.

Ну так я же сказал - не полное, а почти полное. То есть нету сущей мелочи (не особо существенной).

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

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

Потому что ты ищешь неподвижную точку НЕ ТОГО.

Я же говорю - простая функция, на Хаскеле - «\arg -> (1, fst arg)», на схеме чуть более громоздко - "(lambda (arg) (list 1 (car arg)))". В чём проблема-то?

Ну как нету? если я выведу на экран сперва «1», а потом «2», то это не то же самое, что вывести наоборот. Значит - влияют.

Порядок вывода «1» и «2» отлично специфицируется.

Ну вот именно, зачем исправлять, если он не важен? А статическая типизация заставляет исправлять.

Ты всё ещё про REPL? Тогда ты лжёшь. Опять.

То есть ты написал какой-то нерабочий код на схеме, который ничего не делает.

Я предполагал, что ты знаешь, что такое Y-комбинатор.

Ты же не просил рабочий код (собственно, я и утверждаю, что его не бывает). Ты просил спецификацию. Вот она.

Не работает:

Ну дык ты же хаскеля не знаешь.

data Showable where Showable :: Show a => a -> Showable
map (\(Showable a) -> show a) [Showable 1, Showable "2"]

Я лучше знаю, о чем говорил. И я говорил о хаскеле.

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

Я и не говорил, что есть.

Опять лжёшь. [scheme][haskell][oop][fp] Мысли вслух (комментарий) - цитирую: «Иначе бы в хаскеле не было rank-n.»

Или русского ты тоже не знаешь?

Ну вот, ты же сам сказал - аналог большой (полиморфной) лямбды.

В расширениях. И?

С самого начала я лишь опустил rank-1, полагая, что всем все понятно из контекста.

Ну, то есть, спиздел.

Любое слово - многозначное.

В математике - не любое.

Ну так я же сказал - не полное, а почти полное.

То есть, от единицы до бесконечности - это так, мелочь? Главное - от нуля до единицы?

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

Потому что ты ищешь неподвижную точку НЕ ТОГО.

Ну и что что не того? Вот я попробовал найти - он нихуя не ищет. Значит, в хаскеле fix тоже зашкварен.

Я же говорю - простая функция, на Хаскеле - «\arg -> (1, fst arg)», на схеме чуть более громоздко - "(lambda (arg) (list 1 (car arg)))". В чём проблема-то?

А почему именно такая? Я вот хочу другую простую ф-ю (указана выше), в чем проблема-то?

Суть-то в том, что в ленивом ЯП Y-комбинатор точно такой же уебщиный и нихуя не работает для произвольных ф-й. Просто в ленивом ЯП выполняется равенство fst (x y) = x, а в энергичном - нет. Двойственно - в энергичном ЯП выполняется (а в ленивом - нет) равенство f (if x then y else z) = if x then (f y) else (f z). То есть в ленивом ЯП у нас честные произведения, а в энергичном - честные суммы.

Порядок вывода «1» и «2» отлично специфицируется.

Но он не специфицируется в ленивом хаскеле, он специфицируется в энергичном IO.

Ты всё ещё про REPL?

А с реплом проблема другая - репл не заставляет исправлять, он просто не дает переопределить функцию. То есть просто заметание проблемы под ковер.

Ты же не просил рабочий код (собственно, я и утверждаю, что его не бывает). Ты просил спецификацию. Вот она.

С каких пор нерабочий код является спецификацией? Что этот нерабочий код (как спецификация) должен обозначать? Как проверить ф-ю, которую я напишу, на соответствие этой спецификации?

Ну дык ты же хаскеля не знаешь.

И что, мне пидорасить кучу бойлерплейта для любого набора тайпклассов? Выразительный хаскиль такой выразительный. Это не говоря уже о том, что будет, если мне надо абстрагироваться от show.

Опять лжёшь. [scheme][haskell][oop][fp] Мысли вслух (комментарий) - цитирую: «Иначе бы в хаскеле не было rank-n.»

Это не про хаскель-98, очевидно. Следи за контекстом.

В расширениях. И?

Ну да, в расширениях. И?

Ну, то есть, спиздел.

Нет, просто предположил, что собеседнику понятны общепринятые допущения.

В математике - не любое.

И SystemF к этим «не любым» не относится.

Главное - от нуля до единицы?

Именно так. rank-2 тоже достаточно важен, конечно (хотя и намного менее важен, чем rank-1), а вот все, что выше - уже вообще практически не влияет на выразительность.

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

А почему именно такая?

Потому что на ней нормальный Y-комбинатор работает.

нихуя не работает для произвольных ф-й.

Работает для произвольных. Только порой найденная неподвижная точка оказывается жопой (что соответствует зацикливанию или вылету).

А вот в энергичных языках не работает вообще ни для каких функций, кроме, разве что, констант.

Просто в ленивом ЯП выполняется равенство fst (x y) = x, а в энергичном - нет.

Надеюсь, ты имел в виду «fst (x, y) = x»? Ну дык оно и в энергичных выполняется. В них другое не выполняется, принцип подстановки.

Но он не специфицируется в ленивом хаскеле, он специфицируется в энергичном IO.

IO не язык, к нему термин «энергичный» неприменим.

А с реплом проблема другая - репл не заставляет исправлять, он просто не дает переопределить функцию.

Ты же переопределил. Значит, даёт.

С каких пор нерабочий код является спецификацией? Что этот нерабочий код (как спецификация) должен обозначать?

Если бы ты мог сделать нормальный Y-комбинатор (чего ты не можешь), то код сразу стал бы рабочим.

И что, мне пидорасить кучу бойлерплейта для любого набора тайпклассов?

Нет, конечно. Во-первых, требование «вынь да положь мне гетерогенные списки» само по себе странное и в природе крайне редко встречается. Во-вторых, начиная с 7.4.1 можно делать абстракцию по классам, так что всё хорошо.

Это не про хаскель-98, очевидно.

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

Нет, просто предположил, что собеседнику понятны общепринятые допущения.

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

И SystemF к этим «не любым» не относится.

Относится.

Именно так. rank-2 тоже достаточно важен, конечно (хотя и намного менее важен, чем rank-1), а вот все, что выше - уже вообще практически не влияет на выразительность.

Ну пиздёжь ведь, наглый. System F с rank-1 вообще становится незамкнутой.

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

Потому что на ней нормальный Y-комбинатор работает.

Почему же для указанной мной ф-и не сработал?

Работает для произвольных. Только порой найденная неподвижная точка оказывается жопой (что соответствует зацикливанию или вылету).

Ну так и мой комбинатор для энергичного ЯП тоже работает точно так же.

А вот в энергичных языках не работает вообще ни для каких функций, кроме, разве что, констант.

Ложь.

Надеюсь, ты имел в виду «fst (x, y) = x»?

Да, конечно.

Ну дык оно и в энергичных выполняется.

Нет. В энергичном ЯП fst (x, _|_) = _|_ != x. Проблема вся в том, что это равенство: (x, _|_) = _|_ либо выполняется, либо нет, и в зависимости от этого факта в языке ломаются либо произведения (энергичный случай), либо суммы (ленивый случай).

O не язык, к нему термин «энергичный» неприменим.

«энергичный» и «ленивый» применимо не только для ЯП, а для всего, для чего применимо понятие редукции. То есть для любой сущности, которая представляет вычисления. Способ представления этой сущности (символы какого-либо алфавита или что другое) - не важен.

Ты же переопределил.

Нет:

Prelude> let f x = x
Prelude> let g x = f x
Prelude> g 10
10
Prelude> let f x = x + 1
Prelude> g 10
10
Prelude>
не переопределяется :(

Нет, конечно.

Ну как это нет?

Во-вторых, начиная с 7.4.1 можно делать абстракцию по классам, так что всё хорошо.

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

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

Вот именно. И ответ про rank-n был именно про хаскель с расширениями.

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

Есть, конечно. Ну например - арифметика непротиворечива.

Относится

Как показал этот тред - нет.

System F с rank-1 вообще становится незамкнутой

Но это на практике не существенно и на выразительность влияет весьма слабо.

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

Почему же для указанной мной ф-и не сработал?

Такая функция. Ты ещё на (+ 1) попробуй. В смысле, на (lambda (arg) (+ arg 1)).

Ну так и мой комбинатор для энергичного ЯП тоже работает точно так же.

Твой комбинатор выдаёт жопу всегда.

Ложь.

Опровергни примером.

В энергичном ЯП fst (x, _|_) = _|_ != x.

Что такое (_|_) в энергичном языке?

«энергичный» и «ленивый» применимо не только для ЯП, а для всего, для чего применимо понятие редукции. То есть для любой сущности, которая представляет вычисления.

Неслабо передёрнул. Ассемблер тоже занимается редукциями?

не переопределяется

Так ты ж не g переопределял, а f.

С еще большим количеством костылей и бойлерплейта.

Опять пиздёжь.

Написать же функцию map, которая будет просто _работать_, в хаскеле никак нельзя.

Prelude.map.

И ответ про rank-n был именно про хаскель с расширениями.

Ты заврался. Приводи аккуратно диалог: какой ответ, на какую реплику и т.п.

Есть, конечно. Ну например - арифметика непротиворечива.

Нет такого «допущения».

Как показал этот тред - нет.

Этот тред просто показал, что ты не знаешь, что такое System F. Математического определения это не касается никак.

Но это на практике не существенно и на выразительность влияет весьма слабо.

Какая ещё практика, когда речь идёт о System F? У неё есть широко используемые реализации?

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

Такая функция. Ты ещё на (+ 1) попробуй. В смысле, на (lambda (arg) (+ arg 1)).

То есть он работает не всегда.

(define fact (fix (lambda (fact) (lambda (n) (if (= n 0) 1 (* n ((fact) (- n 1)))))))), (fact 5) -> 120. Норм работает.

Опровергни примером.

Смотри выше.

Что такое (_|_) в энергичном языке?

Функция, которая виснет или бросает исключение, например.

Неслабо передёрнул. Ассемблер тоже занимается редукциями?

Конечно.

Так ты ж не g переопределял, а f.

А g вызывает f. Речь идет о том, что без подобного переопределения экспериментировать с кодом нельзя - придется все ф-и перекомпилировать. А чтобы перекомпилировать, придется сделать так, чтобы с типами было все ок.

Опять пиздёжь.

Датычо? Ну-ка попробуй сделай и посмотри, что у тебя выйдет.

Prelude.map.

Так не работает! map show [1, «2»]. Уже выяснили же.

Ты заврался. Приводи аккуратно диалог: какой ответ, на какую реплику и т.п.

С какой стати я должен доказывать, что не верблюд? Ты и доказывай - приводи аккуратно диалог, кто на что отвечал и так далее.

Нет такого «допущения».

Если не делать такого допущения (о непротиворечивости), то вся математика ВНЕЗАПНО становится ненужной.

Этот тред просто показал, что ты не знаешь, что такое System F. Математического определения это не касается никак.

Он показал, что я знаю не только, что такое System F, но и что подразумевается под этой системой в том или ином контексте. А вот ты как раз петушок, который путает обычную System F с System F_w.

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

Смотри выше.

I stand corrected. Не работает, кроме случаев а) когда тип данных, который требуется найти - функция, и б) когда то, к чему применяется fix - константа.

Есть другие случаи.

Функция, которая виснет или бросает исключение, например.

Не понял. То есть, (lambda () (/ 1 0)) = (_|_)? (Ну, условно говоря - я не помню, бросает ли исключение конструкция (/ 1 0)).

Конечно.

Тогда определи понятия «строгий» и «ленивый». Потому что стандартное понимание к ассемблеру, например, неприложимо.

А g вызывает f.

Но ты её не переопределял. Так что она продолжает работать как раньше.

А чтобы перекомпилировать, придется сделать так, чтобы с типами было все ок.

Так если, как ты говоришь, эта функция неважна - нафиг её перекомпилировать?

Так не работает! map show [1, «2»]. Уже выяснили же.

Уже приведён правильный синтаксис. Ты не знаешь Хаскеля, так что учись у тех, кто знает.

С какой стати я должен доказывать, что не верблюд?

С такой, что ты неоднократно продемонстрировал свою верблюжью сущность.

Если не делать такого допущения (о непротиворечивости), то вся математика ВНЕЗАПНО становится ненужной.

Чушь собачья. Кассирше, подсчитывающей сдачу, абсолютно пофиг, какие там допущения куда накладываются.

Он показал, что я знаю не только, что такое System F, но и что подразумевается под этой системой в том или ином контексте.

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

путает обычную System F с System F_w.

Я ни в какой момент не упоминал Fw. Вообще.

Хотя в одном месте надо было (но на это место ты не возразил). Догадаешься?

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

Во-вторых, начиная с 7.4.1 можно делать абстракцию по классам, так что всё хорошо.

Хреновый аргумент, так как это не хаскель, а реализация.

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

никто не пользуется тем хаскелем, который не эта реализация

И что же отсюда следует?

Месьё, в контексте топика сравнивалась схема и хаскель. Реализация свободнее в плане добавления новых фич, которых еще нет в языке (и неизвестно будут ли). Сей переход, как аргумент спора, в этом случае вряд ли корректен.

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

Ты со своим racket везде без мыла пролезешь, но вопрос-то был «что реализует GHC».

Какой racket? А чем же ответ не устраивает?

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

Просто для справки - хаскелем может называется любое надмножество Haskell 98. Это позиция авторов стандартов. Х98 или 2010 не подразумеваются неявно, на то, что имеется в виду именно они нужно явно указывать.

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

Какой racket?

«Много чего, а в частности, костыли.»

А чем же ответ не устраивает?

Фактической неправильностью. GHC реализует не «много чего», а хаскель.

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