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

ну и как она - проще схемы?

Ну я же писал «Более явный синтаксис» - много разного сахара.

Но речь вроде перескочила с семантики на грамматику. Грамматика, да, много сложней. Но насчёт семантики - если взять голый Core, он очень простой, проще схемы с её мутабельностью и call/cc. Если начать рассматривать переменные в IO, конкурентность, STM... Но это уже фичи рантайма реализации позиционирующей себя как полезной для real world задач.

Я говорил об общем понимании как и почему

А какие тогда проблемы (если речь не про что-то вроде «термы ио после компиляции»)? Есть какие-то примеры способные показать нетривиальность хаскельного IO? (бывает, кстати, иногда оставишь ио в ио и действие не выполнится - лишний fmap или return, например, но исчезает если писать сигнатуры).

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

ПОСЛЕ запуска.

Важно, что и в случае статического и в случае динамического ЯП это произойдет во время отладки. причем не факт, что в динамическом ЯП ошибка будет найдена позже - вполне вероятно, что раньше.

С некоторой вероятностью.

Как и статике.

А это тут при чём? Компилятор схемы тоже может быть забагованным.

Ну если тебе никто не гарантирует корректность тайпчекера, то никто не гарантирует, что ошибка не будет пропущена.

Хорошо, реквестирую способ заранее узнать о том, что эти a и b нужно уметь сравнивать. В Схеме. Не залезая в реализацию.

Первый же тест, на котором оно упадет? Документация?

Не понял, в схеме нет nil-а?

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

Врёшь.

Нет, чистая правда.

Что здесь могут использоваться Dynamic или unsafePerformIO.

Ну считай что в динамическом ЯП в каждом модуле такая аннотация. Any problem?

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

А зачем? То есть, я имею ввиду - какая разница, будет ли найдена ошибка во время статической отладки или во время динамической?

И? Пользователю от этого не легче.

Как не легче? Если ошибка найдена не у пользователя - очевидно, ему легче :)

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

При слове «лямбда».

Так слово или буква? Ты определись.

К чему ближе?

К «лямбдам над типами» же.

Нет. Ещё раз: в System F конструкции вида $\Lambda\alpha...$ имеются, в Хаскеле их нет.

Ты тупой чтоли? Эта конструкция - это определение полиморфной функции. Именно в нее рассахариваются определения полиморфных ф-й на хаскеле. Специально для особо одаренных: «SystemF» - это синоним для «типизированное лямбда-исчисление с полиморфизмом».

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

Этот конкретный пример? Сомневаюсь, что он там может быть.

Ты не понял, автор этого треда именно из OOPLAI брал все свои примеры. Так что он там будет :)

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

А я даже знаю, почему!

Потому, что её там нет. Да простит меня Тарантино за это заимстоваание.

Какой луп? Всмысле - что он должен делать?

Я же написал. Тип "((a, z) -> (b, z)) -> (a -> b)".

И с какого хуя это должно работать, если arg должен быть ф-ей, а ты к нему car применяешь?

С какого хуя arg должен быть функцией? Вот, в Хаскеле:

fix (\arg -> (1, fst arg))
и всё отлично работает.

Правда?

Угу.

приведи мне операционную семантику хаскеля, из которой ясно - да, совершенно строго, сперва выведет «1», потом «2».

Лень. Лучше ты приведи случай, когда это не так.

Докажи.

По определению.

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

Назови какую-нибудь хаскельную книгу, являющуюся примерным аналогом сикпа.

Paul Hudak: «The Haskell School of Expression: Learning Functional Programming through Multimedia». Это не прямой аналог, но по духу близко. Может ещё что-то есть, не знаю (http://www.haskell.org/haskellwiki/Books). Наверно, по компиляторам лучше читать книжки про компиляторы («Modern Compiler Implementation in ML», например), по CAS - книжки по CAS, то же самое про схемотехнику, конкретные языки, алгоритмы энергичные / ленивые. Ну и ещё есть множество областей которые не освещены в SICP но при этом не менее, а может и более, важны чем, например, написание метациклического интерпретатора (те же конкурентность и персистентность данных).

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

не факт, что в динамическом ЯП ошибка будет найдена позже - вполне вероятно, что раньше.

Ты хочешь сказать, что запуск программы произойдёт раньше её компиляции?

Как и статике.

Нет, в статике - с гарантией.

Ну если тебе никто не гарантирует корректность тайпчекера, то никто не гарантирует, что ошибка не будет пропущена.

А равно никто не гарантирует, что комп не улетит на Луну сам по себе.

И кто доводит до абсурда?

Первый же тест, на котором оно упадет?

Не «заранее». Наоборот, «постфактум».

Документация?

Молодец. Теперь сделай следующий шаг: попробуй хотя бы часть документации сделать автоматически проверяемой на корректность.

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

Я и не говорил про false.

Ну считай что в динамическом ЯП в каждом модуле такая аннотация. Any problem?

Да. В статическом языке тимлид может бить по рукам за такую аннотацию. VCS может не пропускать такое. В динамическом - не может, ибо тогда нужно бить по рукам всех и реджектить весь код (что, конечно, идея неплохая, но всё-таки).

А зачем?

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

какая разница, будет ли найдена ошибка во время статической отладки или во время динамической?

При статической отладке - с гарантией, при динамической - с вероятностью.

Если ошибка найдена не у пользователя - очевидно, ему легче :)

Если ошибка найдена У ПОЛЬЗОВАТЕЛЯ - ему не легче от того, что это уже не во время отладки.

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

Ну да, и SICP тоже нужно читать, хотя бы раз.

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

Но речь вроде перескочила с семантики на грамматику. Грамматика, да, много сложней.

nuff said.

Но насчёт семантики - если взять голый Core, он очень простой, проще схемы с её мутабельностью и call/cc.

А в этой семантике описывается, как вычисляется IO? Просто если нет - то тогда это не семантика, а бесполезный огрызок. Давай тогда из схемы тоже уберем set! (кстати, семантика set! через ref весьма просто описывается) и call/cc у нас останется тогда по сути обычная лямбда и там и там, ведь так? То есть вопрос именно в семантике всех этих «фишек».

А какие тогда проблемы (если речь не про что-то вроде «термы ио после компиляции»)?

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

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

Так слово или буква? Ты определись.

Слово «лямбда» обозначает одну из букв греческого алфавита. Что непонятно?

К «лямбдам над типами» же.

Нет. Не ближе.

Эта конструкция - это определение полиморфной функции.

Да. И что? В Хаскеле НЕТ таких определений.

Именно в нее рассахариваются определения полиморфных ф-й на хаскеле.

Нет. Они НЕ МОГУТ в неё рассахариваться, ибо такая конструкция в Хаскеле не существует.

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

Ты не понял, автор этого треда именно из OOPLAI брал все свои примеры. Так что он там будет :)

Ну, если ты уверен, давай точную ссылку.

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

Потому, что её там нет.

А ты слева нужный пункт выбери. Вдруг появится?

Я же написал. Тип "((a, z) -> (b, z)) -> (a -> b)".

a - это Top, (a, b) - Top, a -> b - Top, то есть «Top -> Top -> Top» = «Top -> Top» = «Top». Ну тогда например:

(define x 1). Тип совпадает с требуемым.

С какого хуя arg должен быть функцией? Вот, в Хаскеле:

А ну ладно. С точностью до синтаксического сахара:

(define (fix f) (lambda () (f (fix f))))

Вычисляет неподвижную точку. В том числе и для (\arg -> (1, fst arg)).

Лень.

То есть ты не можешь?

Лучше ты приведи случай, когда это не так.

Так тот пример, что ты сам указал. print «1» >> print «2». Ничего не выводит, возвращает IO ().

По определению.

По какому?

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

Ты хочешь сказать, что запуск программы произойдёт раньше её компиляции?

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

Нет, в статике - с гарантией.

Нет, ведь у тебя нету гарантий корректности тайпчека.

А равно никто не гарантирует, что комп не улетит на Луну сам по себе.

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

И кто доводит до абсурда?

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

Не «заранее». Наоборот, «постфактум».

Не «заранее» и не «постфактум». А «во время». во время отладки. И в статике и в динамике. Совершено одинаково.

Молодец. Теперь сделай следующий шаг: попробуй хотя бы часть документации сделать автоматически проверяемой на корректность.

Ценой динамичности и выразительности? Слишком дорого, извини.

Я и не говорил про false.

А про что ты говорил?

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

Прекрасно. Отладка - исправление ошибок, во время тайчека - статическая, во время рантайма - динамическая.

При статической отладке - с гарантией, при динамической - с вероятностью.

В обоих случаях - с вероятностью. В случае статической - вероятность отлавливания ошибки выше. Открыты вопросы - а является ли вероятность при динамической отладке недостаточной (практика говорит, что для 99% таки не является) и стоит ли повышение вероятности огромных ограничений ЯП в выразительности и удобства написания программы.

Если ошибка найдена У ПОЛЬЗОВАТЕЛЯ - ему не легче от того, что это уже не во время отладки.

Не легче, но система типов тут при чем?

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

Смотри - надо указать, что ф-и типа print не исполняют сайд-эффект, а возвращают некую программную сущность, его инкапсулирующую, которая уже потом как-то атсрально исполняется.

Зачем?

int puts(const char *s);

...

    int z = 1 + puts("side effect");
foreign import ccall "puts" puts :: CString -> CInt

...

z <- (1 +) <$> puts <$> newCString "side effect"
z
side effect
13

разница только в том, что мы начали типизировать эффекты (... -> IO (...) - есть эффект, иначе - нет). Если бы в си это было (io int), то напрямую нельзя бы было сделать 1 + puts (предполагается, что `+' - чистая), но были бы те же типизированные применения. Т.е. я тут применяю IO в духе обычного императивного языка, но мне приходится использовать комбинаторы/do-нотацию/single assigment (в си с io точно также бы пришлось использовать спец. формы соответствующие (<$>) и т.п.). Появляется «спецификатор типа» (как на него можно смотреть, для простоты) IO и, как следствие (совать (IO t) на место t не получится - обычная строгая типизация), комбинаторы позволяющие составлять _секвенции_ из операций IO. Вот в си оператор точки-с-запятой же не ругают, хотя он составляет вычисления точно так же как (>>).

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

А ты слева нужный пункт выбери. Вдруг появится?

А зачем? Ты же утверждал, что вся грамматика Схемы на той странице.

a - это Top, (a, b) - Top, a -> b - Top, то есть «Top -> Top -> Top» = «Top -> Top» = «Top».

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

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

Вычисляет неподвижную точку.

Не-а, не вычисляет. Опять-таки, выход этой фигни - всегда функция, и для \arg -> (1, fst arg) это не просто не неподвижная точка, это вообще не то, что входит в область определения.

То есть ты не можешь?

Почему же? Могу, но не вижу смысла.

Так тот пример, что ты сам указал. print «1» >> print «2». Ничего не выводит, возвращает IO ().

Который выводит «1» и «2» обязательно в этом порядке.

Что ты, вообще, пытаешься доказать?

По какому?

По определению результата.

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

Слово «лямбда» обозначает одну из букв греческого алфавита. Что непонятно?

то есть «лямбда над типами» = «одна из букв греческого алфавита над типами»? Ты \то имел ввиду? и что это значит?

Нет. Не ближе.

А что ближе к лямбдам над типами?

Да. И что? В Хаскеле НЕТ таких определений.

Как нет, если есть? Например (\x -> x) - эквивалентно LAMBDA X. lambda x:X. x : X -> X (то етьс это в точности то же самое, просто записано короче). Или ты хочешь сказать, что не эквивалентно? Кстати, следуя твоей логике, просто типизированного лямбда-исчисления в хаскеле тоже нет, ведь там нету конструкций вида lambda x:Type.fun. Зато в хаскеле есть нетипизированное лямбда-исчисление, т.к. коycтрукции lambda x.fun присутствуют! breaking news просто.

Нет. Они НЕ МОГУТ в неё рассахариваться, ибо такая конструкция в Хаскеле не существует.

Я не совсем понимаю, что ты пытаешься доказать. Вот есть лямбда-куб, там есть типизированное лямбда-исчисление с полиморфизмом. Ты хочешь сказать, что SystemF - это не оно (ну раз утверждаешь, что «лямбды над типами - не полиморфизм»)? К слову «лямбда над типами» - это лямбда, которая принимает тип и возвращает тип, то есть f_w. Хватит уже вилять жопой, просто признай, что ошибся, и все.

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

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

Разве что если это C++.

Нет, ведь у тебя нету гарантий корректности тайпчека.

Блин, ну кончай доводить до абсурда, а? Да, у меня нет гарантии, что я не скончаюсь от сердечного приступа до того, как допишу программу. Эти риски одинаковы для всех случаев, а потому несущественны.

А «во время». во время отладки. И в статике и в динамике. Совершено одинаково.

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

Ценой динамичности и выразительности? Слишком дорого, извини.

А ты сделай, не теряя выразительность.

А про что ты говорил?

Про nil.

Отладка - исправление ошибок, во время тайчека - статическая, во время рантайма - динамическая.

OK.

Открыты вопросы - а является ли вероятность при динамической отладке недостаточной (практика говорит, что для 99% таки не является)

Зависит от того, что ты пишешь.

Если хелловорлд, то да, не является.

Если хотя бы то определение sum из SICP-а - то, очевидно, является.

стоит ли повышение вероятности огромных ограничений ЯП в выразительности и удобства написания программы.

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

Не легче, но система типов тут при чем?

При том, что она такую ошибку отловит ДО того, как программа вообще будет готова (в смысле, откомпилирована и вообще доведена до пригодного к шиппингу состояния). И, значит, подобное НЕ МОЖЕТ случиться у пользователя.

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

Зачем?

Потому что это так и есть.

разница только в том, что мы начали типизировать эффекты (... -> IO (...) - есть эффект, иначе - нет).

Неправильно. В этом случае для грязных ф-й, возвращающих IO, не будут исполняться законы функтора, например. А они исполняются. То есть «просто специфицируем типом грязную ф-ю» и «инкапсулируем сайд-эффект внутри представления некоего вычисления» - это принципиально разные с точки зрения семнатики вещи и путать их нельзя ни в оем разе.

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

Вот в си оператор точки-с-запятой же не ругают, хотя он составляет вычисления точно так же как (>>).

Потому что точка с запятой в си - это не оператор и он не занимается составлением вычислений.

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

то есть «лямбда над типами» = «одна из букв греческого алфавита над типами»? Ты \то имел ввиду?

Нет.

А что ближе к лямбдам над типами?

Посмотри, наконец, на ту страничку в википедии. Или хоть на рисунок с неё.

Например (\x -> x) - эквивалентно LAMBDA X. lambda x:X. x : X -> X

Только второе - это вообще не Хаскель. Ну НЕТ в нём таких конструкций. Особенно - в стандартном.

следуя твоей логике, просто типизированного лямбда-исчисления в хаскеле тоже нет, ведь там нету конструкций вида lambda x:Type.fun

Синтаксические различия.

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

Я хочу сказать, что система типов Haskell 98 не содержит System F. Вот и всё. И не может содержать, иначе в System F был бы возможен вывод типов.

Если убрать рекурсивные типы, то система типов Haskell 98 станет строго слабее System F.

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

Мне лень.

Ну, значит, писать в проекте тебе это тоже будет лень. То есть, оно есть, но на практике не используется.

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

А зачем? Ты же утверждал, что вся грамматика Схемы на той странице.

Хватит тупить.

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

Я не увиливаю, я пытаюсь узнать, чего ты от меня хочешь. Дай описание ф-и, которую надо написать. Не тип, а описание ф-и. Чтобы было понятно, что она делает. Спецификацию. А то по типу я могу миллион ф-й написать таких.

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

выяснили? Где?

Не-а, не вычисляет.

Вычисляет.

Опять-таки, выход этой фигни - всегда функция

В данном случае это враппер вокруг чего угодно, то есть результат надо взять в скобки еще раз - ((fix fun)) вместо (fix fun). Просто синтаксический сахар. И там хоть ф-я хоть не-функция будет.

и для \arg -> (1, fst arg) это не просто не неподвижная точка, это вообще не то, что входит в область определения.

Для \arg -> (1, fst arg) это будет бесконечный список из единиц - то есть неподвижная точка \arg -> (1, fst arg).

Который выводит «1» и «2» обязательно в этом порядке.

Но при чем тут хаскель? Это уже язык под названием IO () (энергичный, естественно). Кстати, где можно ознакомиться с его спеками?

Что ты, вообще, пытаешься доказать?

Что в ленивом ЯП нельзя специфицировать порядок вычислений. То есть - нельзя написать никакой полезной программы. Вот даже тот же хаскель - вместо того, чтобы писать полезные программы на хаскеле, мы пишем программы на хаскелле, которые генерируют полезные программы на энергичном IO (). Ну мы с тем же успехом могли бы на хаскеле генерировать асм-код для конкретного процессора и потом его выполнять. И потом хвалиться «смотрите, мы пишем на ленивом ЯП!».

По определению результата.

И где это определение?

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

Разве что если это C++.

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

Блин, ну кончай доводить до абсурда, а? Да, у меня нет гарантии, что я не скончаюсь от сердечного приступа до того, как допишу программу. Эти риски одинаковы для всех случаев, а потому несущественны.

Ну так и риски того, что ошибка не будет поймана тестами - тоже несущественны. Хоть и выше.

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

Какая разница, до или после запуска? По факту? Чем «до запуска» лучше, чем «после»? С точки зрения программиста - он жмет кнопку «тайпчек» и видит ошибку. Или жмет кнопку «прогнать тесты» - и видит ошибку. Какая разница?

А ты сделай, не теряя выразительность.

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

Про nil.

И что nil?

Зависит от того, что ты пишешь.

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

Если хотя бы то определение sum из SICP-а - то, очевидно, является.

А я вижу, что, очевидно, да. И если там будет ошибка (при написании sum) то на первом же тесте оно упадет.

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

Не предлагают, но почему тогда делают?

При том, что она такую ошибку отловит ДО того, как программа вообще будет готова (в смысле, откомпилирована и вообще доведена до пригодного к шиппингу состояния). И, значит, подобное НЕ МОЖЕТ случиться у пользователя.

Отладка в общем отловит такую ошибку до того, как программа будет готова.

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

Хватит тупить.

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

Чтобы было понятно, что она делает. Спецификацию.

Вот тебе спецификация:

loop f = \a -> let (b, z) = f (a, z) in b
Всё.

А то по типу я могу миллион ф-й написать таких.

Кстати, не можешь. Тебе пришлось скастовать всё к какому-то Top. В Хаскеле такое не пройдёт, google://free+theorems.

выяснили?

Ну, ты привёл то, что тебе казалось Y-комбинатором, а оно не работает. На первом же примере.

В данном случае это враппер вокруг чего угодно

Угу, враппер. А не неподвижная точка.

то есть результат надо взять в скобки еще раз

Ну, попробуй. На том же примере: ((fix (lambda (arg) (list 1 (car arg))))). Навернётся с сообщением об ошибке.

Для \arg -> (1, fst arg) это будет бесконечный список из единиц

С чего бы? Неподвижная точка этой функции - список из ДВУХ единиц: (1, 1). См. хаскельный вариант.

Но при чем тут хаскель? Это уже язык под названием IO ()

Что ты говоришь? И на каком алфавите этот язык?

Что в ленивом ЯП нельзя специфицировать порядок вычислений.

Отлично. Встречный аргумент: и нафиг не надо, результат от порядка не зависит.

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

Нет.

То есть видимо буквы (и слова, обозначающие буквы) тут не при чем. Так какого хуя ты пургу гнал?

Посмотри, наконец, на ту страничку в википедии.

Смотрю и вижу определение параметрического полиморфизма, что дальше?

Только второе - это вообще не Хаскель. Ну НЕТ в нём таких конструкций. Особенно - в стандартном.

Зато есть конструкции, которые полностью. эквивалентны.

Синтаксические различия.

Между (\x -> x) и LAMBDA X. lambda x:X. x : X -> X тоже только синтаксические различия. Оба этих терма вычисляются в одно и то же - полиморфную функцию, которая возвращает свой аргумент неизменным.

Я хочу сказать, что система типов Haskell 98 не содержит System F. Вот и всё. И не может содержать, иначе в System F был бы возможен вывод типов.

Ты вообще за разговором следишь? Вывод типов в SystemF не возможен только с rank-n полиморфизмом, а с rank-2 - вполне. В контексте хаскеля под SystemF подразумевается всегда SystemF с rank-2, которую хаскель-98 строго содержит (как раз те самые LAMBDA X. lambda x:X. x : X -> X - это rank-1 полиморфизм).

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

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

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

Потому что точка с запятой в си - это не оператор и он не занимается составлением вычислений.

6.4.6.

Компилятор делает так:

assemble(x ; y) = assemble(x) ++ assemble(y)
quasimoto ★★★★
() автор топика
Ответ на: комментарий от anonymous

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

В ghc есть полноценный репл. Начиная с 7.4.1 - совершенно полноценный.

Ну так и риски того, что ошибка не будет поймана тестами - тоже несущественны. Хоть и выше.

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

Чем «до запуска» лучше, чем «после»?

Тем, что «до запуска» ловится гарантированно (да, с поправкой на бажность компилятора), а «после» - с вероятностью (плюс та же поправка).

Всмысле, написать свой ЯП?

Не хочешь, возьми Хаскель. В нём всё уже сделано.

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

Да. А мы плавно перешли на «задачи требуют»?

И если там будет ошибка (при написании sum) то на первом же тесте оно упадет.

Нет, при использовании.

Не предлагают, но почему тогда делают?

Мы не про C++.

Отладка в общем отловит такую ошибку до того, как программа будет готова.

Что значит «в общем»? «Может быть»? Да, может быть. А может быть, и нет.

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

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

Как они могут быть ни при чём, если мы обсуждаем языки программирования? (Да, System F, естественно, тоже язык программирования).

Смотрю и вижу определение параметрического полиморфизма, что дальше?

Ты видишь конструкцию, которой в Хаскеле нет.

Зато есть конструкции, которые полностью. эквивалентны.

И?

Между (\x -> x) и LAMBDA X. lambda x:X. x : X -> X тоже только синтаксические различия. Оба этих терма вычисляются в одно и то же - полиморфную функцию, которая возвращает свой аргумент неизменным.

Да. Что не отменяет того факта, что в System F конструкция $\Lambda$ есть (и она НЕ СВОДИТСЯ к \x -> x), а в Хаскеле нет.

Вывод типов в SystemF не возможен только с rank-n полиморфизмом, а с rank-2 - вполне.

System F - это System F. В ней нет ограничения на rank.

Если ты хочешь сказать, что в Хаскеле есть подмножество, эквивалентное той маленькой части System F, которая содержит только термы ранга не выше 1, то да, я соглашусь.

В контексте хаскеля под SystemF подразумевается

System F - математический термин. У него НЕТ никаких «подразумевается», и никаких «в контексте».

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

Ну если тебе лень написать одно слово в гугле - почему не должно быть лень мне?

А где я сказал, что не должно? Лень, так лень. Выводы из этого я уже сделал (что это некая хрень, на практике не используемая). Кстати, с выводами о точной операционной семантике хаскеля они совпадают (на практике на неё всем плевать).

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

В этом случае для грязных ф-й, возвращающих IO, не будут исполняться законы функтора, например.

Будут выполняться, если правильно fmap (он же (<$>)) напишешь. Если это будет спец. форма (в си с io - будет, т.к. ФВП нет), то тем более он будет хороший. То же самое про все остальные комбинаторы.

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

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

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

Вот тебе спецификация:

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

Кстати, не можешь. Тебе пришлось скастовать всё к какому-то Top. В Хаскеле такое не пройдёт

Но я же не буду писать на хаскеле, я буду писать на энергичном ЯП, а в нем free theorems работать не будут.

Угу, враппер. А не неподвижная точка.

Чисто синтаксические различия.

Что ты говоришь? И на каком алфавите этот язык?

Не знаю. Сколько у хаскиебов не просил спецификации - никто дать не может.

Отлично. Встречный аргумент: и нафиг не надо, результат от порядка не зависит.

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

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

В ghc есть полноценный репл. Начиная с 7.4.1 - совершенно полноценный.

ну что ж, проверим. полноценный репл:

> (define (f x) x)
> (define (g f) (compose f f))
> (define (h x) ((g f) x))
> (h 10)
10
> (define (f x) (+ x 1))
> (h 10)
12
> 
ghci:
Prelude> let f x = x
Prelude> let g f = f . f
Prelude> let h x = (g f) x
Prelude> h 10
10
Prelude> let f x = x + 1
Prelude> h 10
10
Prelude>

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

И там и там - с вероятностью. Разница только в значении этой вероятности.

Тем, что «до запуска» ловится гарантированно (да, с поправкой на бажность компилятора), а «после» - с вероятностью (плюс та же поправка).

См. выше.

Не хочешь, возьми Хаскель. В нём всё уже сделано.

Не подходит, извини. Хаскель - как раз каноничный пример того, как жертвуют выразительностью ради типизации. Подождем пока, хотя бы, реализация гетерогенных списков не будет темой Ph.D.

Да. А мы плавно перешли на «задачи требуют»?

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

Нет, при использовании.

Пример?

Мы не про C++.

Не знаю, про что ты, а я - про хаскель.

Что значит «в общем»?

«отладка в общем» = и статическая и динамическая.

Да, может быть. А может быть, и нет

ну да, может да а может и нет. В большинстве задач вероятности, с которой «да», нам достаточно.

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

Это делает компилятор, а не сама программа.

Это было семантическое правило (src -> trg) для спец. формы. Всё равно, я не вижу разницы кроме той, что у эффектных функций изменились типы (появился конструктор типа io) и аппликации стало невозможно делать напрямую (типы не совпадают), и стали нужны комбинаторы - у GHC это функции определённые в GHC.* пакетах и скрытые за типо-классовым интерфейсом (этот момент незначителен), в си с io это были бы спец. формы компилятора (практически, и семантические правила вообще).

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

Ты видишь конструкцию, которой в Хаскеле нет.

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

И?

Что и? Если есть конструкция Х, эквивалентная Y, то есть и Y (Y будет синтаксическим сахаром).

Да. Что не отменяет того факта, что в System F конструкция $\Lambda$ есть (и она НЕ СВОДИТСЯ к \x -> x), а в Хаскеле нет.

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

эквивалентное той маленькой части System F

Почему же маленькой? Там есть все кроме полиморфизма высших рангов, то есть практически вся SystemF.

System F - математический термин. У него НЕТ никаких «подразумевается», и никаких «в контексте».

Это в твоем сферическом вакууме. На практике все говорят просто «SystemF», подразумевая rank-2 полиморфизм.

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

Будут выполняться, если правильно fmap (он же (<$>)) напишешь.

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

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

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

Изменяется семантика ЯП и результат исполнения кода. Если это «нет разницы», то ок.

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

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

Понятно, то есть, на одну страницу оно никак не умещается.

Какие именно разделы нужно посмотреть?

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

Понятно, то есть, ты хаскеля просто не знаешь.

Давай хоть с Y-комбинатором разберёмся.

Но я же не буду писать на хаскеле, я буду писать на энергичном ЯП, а в нем free theorems работать не будут.

С оговорками соглашусь - тут важна не столько энергичность (она мешает free theorems, но не сильно), сколько слабая типизация.

Чисто синтаксические различия.

Боюсь, нет. В частности, они приводят к тому, что твой код благополучно падает. На вполне нормальном инпуте.

Не знаю.

Так какого хуя ты это обозвал «языком»?

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

Да, но это не «порядок вычислений».

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

полноценный репл:

К чему ты это? Я и так знаю, что схемовский репл - полное говно. Именно по этой причине.

И там и там - с вероятностью. Разница только в значении этой вероятности.

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

Хаскель - как раз каноничный пример того, как жертвуют выразительностью ради типизации.

Нисколько.

Подождем пока, хотя бы, реализация гетерогенных списков не будет темой Ph.D.

А что ты собираешься там держать такое гетерогенное?

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

Но обсуждаем-то мы не это.

Пример?

Я с него, можно сказать, начал.

Не знаю, про что ты, а я - про хаскель.

Нет, ты сказал «но почему тогда делают?» Это явно про C++.

«отладка в общем» = и статическая и динамическая.

Может поймать. А может и не поймать.

В большинстве задач вероятности, с которой «да», нам достаточно.

Возможно, но заранее учить новичков надеяться на авось - не есть хорошо.

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


Это не означает, что учеников надо грузить теорией.


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


The Haskell Platform 2011.2

Да ладно, у нас на мехмате им студентов травят. Они потом долго плюются.


Vudod (12.03.2011 14:19:58)

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

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

Нет, ты видишь лямбду. Это более общая вещь, чем то, что есть в H98.

Если есть конструкция Х, эквивалентная Y, то есть и Y

Да, но конструкции, эквивалентной большой лямбде, нет.

Эти конструкции сводятся друг к другу и являются синтаксическим сахаром друг над другом.

Чего? $\Lambda$ сводится к \x -> x?

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

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

Там есть все кроме полиморфизма высших рангов, то есть практически вся SystemF.

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

Во-вторых, как ты хотя бы списки закодируешь в System F без полиморфизма высшего ранга?

На практике все говорят просто «SystemF», подразумевая rank-2 полиморфизм.

Я не знаю, как насчёт лисперов, они люди по определению малограмотные, но нормальные люди говоря о математическом понятии подразумевают именно это понятие.

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

Весьма желательно привести список литературы: что нужно прочитать до того, как соваться в Haskell. По лямбда-исчислению, комбинаторной логике и прочему.

Да ну нафиг, не надо этого.

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

Какие именно разделы нужно посмотреть?

7.1 Formal syntax.

Понятно, то есть, ты хаскеля просто не знаешь.

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

С оговорками соглашусь - тут важна не столько энергичность (она мешает free theorems, но не сильно), сколько слабая типизация.

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

Боюсь, нет. В частности, они приводят к тому, что твой код благополучно падает. На вполне нормальном инпуте.

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

Так какого хуя ты это обозвал «языком»?

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

Да, но это не «порядок вычислений».

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

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

К чему ты это? Я и так знаю, что схемовский репл - полное говно. Именно по этой причине.

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

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

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

Нисколько.

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

А что ты собираешься там держать такое гетерогенное?

Да что угодно. Гетерогенное.

Но обсуждаем-то мы не это.

А что тогда?

Я с него, можно сказать, начал.

Линк?

Нет, ты сказал «но почему тогда делают?» Это явно про C++.

Нет, я про хаскель. Там тоже делают. А прблема плюсов совсем не типизация.

Может поймать. А может и не поймать.

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

Возможно, но заранее учить новичков надеяться на авось - не есть хорошо.

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

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

Нет, ты видишь лямбду. Это более общая вещь, чем то, что есть в H98.

Ну да, в haskell-98 она ограничена rank-1 (или rank-2? ну не принципиально).

Да, но конструкции, эквивалентной большой лямбде, нет.

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

Чего? $\Lambda$ сводится к \x -> x?

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

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

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

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

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

Я не знаю, как насчёт лисперов, они люди по определению малограмотные, но нормальные люди говоря о математическом понятии подразумевают именно это понятие.

В математике, если ты не знал, практически всегда упоминаемые понятия завязаны на контекст. Потому что иначе объем статьи/монографии/учебника/чего угодно разрастется в десятки раз.

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

f <- new (id :: Num a => a -> a)

Неправильно! Надо (id :: a -> a). Ну и вырвиглазие пиздец конечно. Ты сам понимаешь - на практике это в подобном виде неюзабельно.

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