Pull to refresh

Comments 42

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

Например, говорится, что к базовым типам относятся Числа, Логические величины, Символы, Списки, Упорядоченные множества (tuples) и Функции. По какому признаку одни типы относятся к базовым, а другие — нет? Возможные видимые варианты ответа:

1. как языковая элементарная конструкция, без которой язык ущербен? С самого начала, даже в официальном описании языка, подчёркивается, что многие типы из Prelude могут быть описаны натуральными средствами языка, например, data Bool = True | False, или data Char = 'a' | 'b' | ... Многие типы из приведённых допускают альтернативное описание. С другой же стороны, IO не может быть описан средствами языка, хотя в списке базовых типов не значится.

2. как реализуемые особым образом типы? Например, список — участок памяти, Bool — всем привычный bool, Int — всем привычный 4-байтный int. Если так, то почему сюда не включен IO? Да и зачем в языке столь высокого уровня с первых шагов акцентировать внимание на такой конкретике.

3. как типы сорта * в противопоставление типам сорта * -> * или более сложных сортов? Да, Int :: *, Char :: *, Bool :: *, но [] :: * -> * уже не подходит.
Так почему именно эти типы были названы базовыми?

Кроме этого момента есть ещё некоторые минусы, вроде смешивание рассказа о языке и о ghci, скачки от одной темы к другой, отсутствие единой линии (плана) повествования, малое число примеров, демонстрирующих объясняющие слова.

Я не знаю, на кого рассчитана статья. И несмотря на это, хорошо, что появляются новые туториалы по языку. Продолжение не будет лишним, но мне кажется, что лучше собрать уже имеющуюся русскоязычную литературу и опираться на неё во избежание повторения уже написанного и изложенного, но ради дополнения.
Попытался скомпоновать так, что бы было понятно тем, кто знаком с императивными языками.
Некоторые темы не затрагивал специально, что бы не выпадать из темы.
Список базовых типов был взят из лекции «как есть» и столь глубокий анализ я, увы, не проводил.
При все этом, признаю замечания справедливыми. В дальнейшем постараюсь исправить.
Дык вот же
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
Для этого в языке требуются «уникальные типы», как в Clean и Mercurry. Без них программист сможет создать новый мир не разрушив старого.
Каким образом, если конструктор RealWorld не экспортируется?
Разработчик библиотеки до него сможет добраться.
Да. Он же тоже человек, нельзя ему такое позволять.
Тогда надо FFI запрещать, там и не такое можно сделать.
не всё так страшно:

{-
This is a generated file (generated by genprimopcode).
It is not code to actually be used. Its only purpose is to be
consumed by haddock.
-}

-- | @State\#@ is the primitive, unlifted type of states.  It has
-- 	one type parameter, thus @State\# RealWorld@, or @State\# s@,
-- 	where s is a type variable. The only purpose of the type parameter
-- 	is to keep different state threads separate.  It is represented by
-- 	nothing at all. 

data State# s

-- | @RealWorld@ is deeply magical.  It is /primitive/, but it is not
-- 	/unlifted/ (hence @ptrArg@).  We never manipulate values of type
-- 	@RealWorld@; it\'s only used in the type system, to parameterise @State\#@. 

data RealWorld
UFO landed and left these words here
Потому что путаете types и kinds.
Prelude> :k []
[] :: * -> *
UFO landed and left these words here
Kinds имеют такое же отношение к types, как types к values. То есть можно считать их типами типов, если это не слишком запутывает.

Разговор про kinds, и все на самом деле достаточно просто. Int и Double имеют kind *, что означает, что они являются типами данных сами по себе. А вот просто списка быть не может — может быть список из Int, список из Double, или список из списков из Double, и т. д., то есть он параметризуется другим типом, поэтому его kind — это * -> *. А вот функция (->) тоже не может быть сама по себе, она из некого типа в другой, поэтому она параметризуется двумя типами, так что ее kind — это * -> * -> *.

Интереснее то, что kind есть не только у типов, но и, например, классов типов. Функтор, например, параметризуется неким типов, kind которого * -> *, и мы получаем некий тип данных, но не совсем любой — про него известно, как минимум, что для него определна операция fmap, что в некотором смысле является ограничением (constraint), так что
Prelude> :k Functor
Functor :: (* -> *) -> Constraint
… как первый язык, говорите…
В любом приличном языке есть сложные вещи. К счастью, все их знать не обязательно — при начальном обучении их можно опустить.
Начальное обучение предполагает знакомство с базовым вводом-выводом, который в хаскеле предполагает понимание монад, что с трудом может быть отнесено к базовым навыкам.
Понимание монад для ввода-вывода не обязательно. Надо понимать, что операция ввода-вывода — first-order value и просто так не выполняется.
Не знал, что kinds применим к классам типов.
Только на многопараметрические классы странно себя ведут. Есть у меня
class Tr t v | t -> v where
  getVal :: t -> v
  getForest :: t -> [t]

:k на него выдает
*Main> :k Tr
Tr :: * -> * -> Constraint

А я ждал что-то типа
 Tr :: (*,*) -> Constraint
Перепутал — скобочки в :k Functor не заметил.
Заметил — все стало логичным.
На счёт основных типов (1, 2).
Булевый тип — не базовый.
Он определён как
data Bool = False | True


Список же определён как
data [a] = [] | a : [a]

То есть обычным способом. Необычность заключается ЛИШЬ в особом синтаксисе типа:
Поскольку по правилам языка его надо было определить так:
data [] a = [] | a :  ([] a)

И к тому же, символы '[', ']' — запрещены к использованию в именах конструкторов.
А в памяти он находится как обычные данные.

Кортежи(туплы) так же используют повторы запятой как конструктора — и только этим они особенные.
А вот числа и символы — да, реализованы отдельно (примитивами языка)

IO — не базовый тип, есть тип IO a — он да, самим языком не описывается. Но ничего особенного в плане размещения в памяти нет.

(3) На счёт сортов, тупл — это многомерный сорт.
(,,,) :: * -> * -> * -> * -> *
(Int,Double) :: *
Maybe :: * -> *
Maybe Int :: *
Так что сортность — не показатель базовости.

Базовость — то, что есть в Прелюдии.
Хотя там есть и
data Ordering = LT | EQ | GT
да, об этом (определения Bool, [], кортежей) тоже стоит упомянуть.
Если под базовостью понимать вхождение в Прелюдию, то стоит упомянуть о Maybe, Ordering и др. Впрочем, автор согласился, что у него только поверхностно описываются «базовые» типы, чем я удовлетворён.

К слову, сказано, что любой тип данных и функция из Прелюдии может быть описана в терминах языка (как Bool или Maybe) или псевдокода (как списки или кортежи) и реализована непредопределённым образом для каждой реализации компилятора, допуская как реализацию напрямую, как написано, так и по собственному разумению.
То есть тот набор типов, которые реализованы отдельно (по Вашим словам) некими примитивами, не определён стандартом, а определяется каждой конкретной реализацией. Этот набор может не ограничиваться Int и Char.
Да, можно попытаться сделать что-то самому, пользуясь не совсем каноническими функциями, такими как:
unsafeCoerce :: a -> b

unsafePerformIO :: IO a -> a

unsafeIOToST :: IO a -> ST s a

unsafeSTToIO :: ST s a -> IO a


А можно попросить девелоперов и использовать расширения языка и прагмы.
Например, использовать не-ленивые типы(Bang patterns) и даже не-коробочные типы (unboxed types).
Для обычного человеку (не математика и не программиста) изучение языка это тренировка порождения и распознавания его конструкций. Как языка программирования, так и естественного. На начальном этапе строгость менее важна, чем возможность попробовать.
Курс построен просто: одно и то же понятие дается несколько раз. Сначала правда, но далеко не вся правда. Потом правда, но уже побольше правды. В конце концов, много правды, но, конечно, не вся.

Не сразу, но довольно быстро студенты узнают, что тип Bool описан в прелюдии и получают наставление читать прелюдию долгими осенними вечерами. Довольно быстро. Но не сразу.

Это курс для новичков. Идеально — для школьников. Я его читал в школе 2101 города героя Москвы. Сейчас: первый курс филиала МГУ в Севастополе. Тоже нормально заходит.

Но даже когда я его читаю 4 курсу ЯрГУ и мне попадается Haskell-профи способный слева направо написать:

msort xs = on merge msort `uncurry` splitAt (length xs `div` 2) xs

и он, и я находим фан в прохождении курса ;-)

Курс не только (и не столько) про Haskell. И в нем много работы студентов. Моя мечта максимально приблизиться к технологии «листочки Н.Н.Константинова» из советских физ.-мат.школ
Функция с несколькими параметрами

Не стоит вводить в заблуждение начинающих изучать язык. В Хаскеле (и в некоторых других (всех?) функциональных языках высшего порядка) все функции имеют ровно один аргумент; говорить, что их несколько — жаргонизм.

Существует два способа имитировать полезную программистам множественность аргументов.
(1) Объявить функцию, возвращающую в качестве результата другую функцию
f :: a -> b -> c, что эквивалентно f :: a -> (b -> c).

(2) Использовать кортеж (tuple) в качестве аргумента:
f :: (a, b) -> c. Здесь (a, b) — это не группа параметров, как в языках наподобие Си, а самодостаточный тип, который можно использовать где угодно, в том числе в качестве возвращаемого результата функции.

Спасибо. Сделал в статье отсылку к Вашему комментарию.
Правда про количество аргументов будет сказана, но просто позже. В разделе про карринг, естественно. Будет сказано про правую ассоциативность ->, про левую ассоциативность $ (которого в курсе нету, просто аппликация, пробел, если угодно). Все будет.

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

Мы находимся в ситуации, когда ни один студент за 15 лет мне ни разу не смог в начале курса дать определение понятия функции. Определение функции, которое развенчает функцию до его нормального состояния — обычного value, ни лучше, и не хуже любого иного value, таких как True, 17, (5, 'a'), [1...10] или что-то еще такое.
Может стоит начать с того для каких задач полезен/удобен этот язык?
там лидируют Data — 790 (что логично) и Web — 529 :)))
Мне тоже интересен этот вопрос. Какое у этого языка преимущество или недостатки по сравнению с другими языками программирования?
В курсе много про это сказано: преимущества, где используется, для чего.

В начале курса про это говорить не с руки. В начале курса говорится только, почему для описания функционального программирования (функциональной парадигмы, декларативного стиля) использован Haskell, а не Erlang, не Refal, не Lisp и т.д. Про это сказано в первой главе. Статья посвящена второй главе, кстати.

А вот мой вопрос знатокам промышленного программирования: какое мобильное приложение держит рекорд по числу скачиваний? И на каком языке оно написано?
Такое ощущение складывается, что Хаскель очень хорошо подошел бы как интерфейс какой-нибудь специализированной NoSQL. С его «ленивыми вычислениями» можно было бы творить чудеса в обработке сложных структур данных (например тот же анализ статистических данных). Или я неправ?
Я правильно понимаю, что (x:xs) определено для массива из одного элемента и не определено для пустого массива?
x : xs — определено для массива 1 и более элементов,
x : y : xs — для 2х и более
x : y : z : xs — для 3х и более
[x] — для одного элемента
[x, y] — для 2х элементов
[x, y, z] — для 3х элементов
[] — для пустого списка
С интересом почитал. Благодарю! Вас нашли сегодняшние студенты из ЯрГУ и используют Ваш конспект для своей подготовки к рубежному контролю.

/* Но, за такое определение функции ребята получат 0 баллов ;-)

Правильно так: Функцией f :: A -> B будем называть такое подмножетсво f декартова произведения A*B, для которого выполнено: для всех (x1,y1)<-f, (x2,y2)<-f выполнено: если x1=x2 то обязательно y1=y2. То есть, функция это частный случай релейшена.

Договоримся писать f x = y если (x,y)<-f. */

Хочу сообщить, что курс жив. Он преподается уже 15 лет без перерыва. И, конечно, он развивается. В лекциях появилось еще больше фрагментов «доброволец к доске». Многие из этих фрагментов связаны с «задачами от А.Шкреда» из Facebook-а. Мы на Haskelle решаем задачки с международных математических олимпиад, шахматные головоломки, математические кроссворды и т.п.

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

Курс не только (и не столько) про Haskell. И в нем много работы студентов. Моя мечта максимально приблизиться к технологии «листочки Н.Н.Константинова» из советских физ.-мат.школ.

В курсе за 4 месяца помимо лекций (во время которых много команд «доброволец к доске») студенты должны написать 114 разных этюдиков на Haskell, прогнать их через сервер автопроверки и потом сдать код на просмотр преподавателю (уже по поводу стиля написания, ведь сервер автопроверки это не проверит).

В этом году летом в сервер автопроверки (он на Haskell-е, конечно) было добавлена обработка исключений и расширены наборы тестов, чтобы проверять, что решения студентов выдают undefined (error или иное) там, где целевая функция не должна быть определена. Работу делал я вместе с парой ребят из Севастополя — вчерашние первокурсники.

Все материалы курса доступны: goo.gl/r2IKNz

Там есть файл «Подготовка к лекциям.docx» в котором есть ссылки на всякое дополнительное (например, на видеозаписи zoom-лекций 2020 в ЯрГУ).

Был бы рад, если добрые люди подкинули бы задачек для самостоятельного программирования. Если возникнет такое желание, то можно посмотреть текущую коллекцию (по ссылке и каталог «задачи») и расширить ее. Что-то из коллекции стоит выкинуть, я знаю, но количество уменьшать не хочу. Поэтому — выкину при замене на более годные.
Sign up to leave a comment.

Articles