Comments 42
В этой статье используется очень прагматичный подход к ознакомлению, которые не затрагивает ряд догматичных аспектов языка.
Например, говорится, что к базовым типам относятся Числа, Логические величины, Символы, Списки, Упорядоченные множества (tuples) и Функции. По какому признаку одни типы относятся к базовым, а другие — нет? Возможные видимые варианты ответа:
1. как языковая элементарная конструкция, без которой язык ущербен? С самого начала, даже в официальном описании языка, подчёркивается, что многие типы из Prelude могут быть описаны натуральными средствами языка, например,
2. как реализуемые особым образом типы? Например, список — участок памяти, Bool — всем привычный bool, Int — всем привычный 4-байтный int. Если так, то почему сюда не включен IO? Да и зачем в языке столь высокого уровня с первых шагов акцентировать внимание на такой конкретике.
3. как типы сорта * в противопоставление типам сорта
Так почему именно эти типы были названы базовыми?
Кроме этого момента есть ещё некоторые минусы, вроде смешивание рассказа о языке и о ghci, скачки от одной темы к другой, отсутствие единой линии (плана) повествования, малое число примеров, демонстрирующих объясняющие слова.
Я не знаю, на кого рассчитана статья. И несмотря на это, хорошо, что появляются новые туториалы по языку. Продолжение не будет лишним, но мне кажется, что лучше собрать уже имеющуюся русскоязычную литературу и опираться на неё во избежание повторения уже написанного и изложенного, но ради дополнения.
Например, говорится, что к базовым типам относятся Числа, Логические величины, Символы, Списки, Упорядоченные множества (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 не экспортируется?
Разработчик библиотеки до него сможет добраться.
Какой библиотеки? base?
не всё так страшно:
{-
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
Потому что путаете types и kinds.
Prelude> :k []
[] :: * -> *
Kinds имеют такое же отношение к types, как types к values. То есть можно считать их типами типов, если это не слишком запутывает.
Разговор про kinds, и все на самом деле достаточно просто. Int и Double имеют kind *, что означает, что они являются типами данных сами по себе. А вот просто списка быть не может — может быть список из Int, список из Double, или список из списков из Double, и т. д., то есть он параметризуется другим типом, поэтому его kind — это * -> *. А вот функция (->) тоже не может быть сама по себе, она из некого типа в другой, поэтому она параметризуется двумя типами, так что ее kind — это * -> * -> *.
Интереснее то, что kind есть не только у типов, но и, например, классов типов. Функтор, например, параметризуется неким типов, kind которого * -> *, и мы получаем некий тип данных, но не совсем любой — про него известно, как минимум, что для него определна операция fmap, что в некотором смысле является ограничением (constraint), так что
Разговор про kinds, и все на самом деле достаточно просто. Int и Double имеют kind *, что означает, что они являются типами данных сами по себе. А вот просто списка быть не может — может быть список из Int, список из Double, или список из списков из Double, и т. д., то есть он параметризуется другим типом, поэтому его kind — это * -> *. А вот функция (->) тоже не может быть сама по себе, она из некого типа в другой, поэтому она параметризуется двумя типами, так что ее kind — это * -> * -> *.
Интереснее то, что kind есть не только у типов, но и, например, классов типов. Функтор, например, параметризуется неким типов, kind которого * -> *, и мы получаем некий тип данных, но не совсем любой — про него известно, как минимум, что для него определна операция fmap, что в некотором смысле является ограничением (constraint), так что
Prelude> :k Functor
Functor :: (* -> *) -> Constraint
… как первый язык, говорите…
В любом приличном языке есть сложные вещи. К счастью, все их знать не обязательно — при начальном обучении их можно опустить.
Начальное обучение предполагает знакомство с базовым вводом-выводом, который в хаскеле предполагает понимание монад, что с трудом может быть отнесено к базовым навыкам.
Не знал, что kinds применим к классам типов.
Только на многопараметрические классы странно себя ведут. Есть у меня
:k на него выдает
А я ждал что-то типа
Только на многопараметрические классы странно себя ведут. Есть у меня
class Tr t v | t -> v where
getVal :: t -> v
getForest :: t -> [t]
:k на него выдает
*Main> :k Tr Tr :: * -> * -> Constraint
А я ждал что-то типа
Tr :: (*,*) -> Constraint
На счёт основных типов (1, 2).
Булевый тип — не базовый.
Он определён как
Список же определён как
То есть обычным способом. Необычность заключается ЛИШЬ в особом синтаксисе типа:
Поскольку по правилам языка его надо было определить так:
И к тому же, символы '[', ']' — запрещены к использованию в именах конструкторов.
А в памяти он находится как обычные данные.
Кортежи(туплы) так же используют повторы запятой как конструктора — и только этим они особенные.
А вот числа и символы — да, реализованы отдельно (примитивами языка)
(3) На счёт сортов, тупл — это многомерный сорт.
Так что сортность — не показатель базовости.
Базовость — то, что есть в Прелюдии.
Хотя там есть и
Булевый тип — не базовый.
Он определён как
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.
Если под базовостью понимать вхождение в Прелюдию, то стоит упомянуть о Maybe, Ordering и др. Впрочем, автор согласился, что у него только поверхностно описываются «базовые» типы, чем я удовлетворён.
К слову, сказано, что любой тип данных и функция из Прелюдии может быть описана в терминах языка (как Bool или Maybe) или псевдокода (как списки или кортежи) и реализована непредопределённым образом для каждой реализации компилятора, допуская как реализацию напрямую, как написано, так и по собственному разумению.
То есть тот набор типов, которые реализованы отдельно (по Вашим словам) некими примитивами, не определён стандартом, а определяется каждой конкретной реализацией. Этот набор может не ограничиваться Int и Char.
Да, можно попытаться сделать что-то самому, пользуясь не совсем каноническими функциями, такими как:
А можно попросить девелоперов и использовать расширения языка и прагмы.
Например, использовать не-ленивые типы(Bang patterns) и даже не-коробочные типы (unboxed types).
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. И в нем много работы студентов. Моя мечта максимально приблизиться к технологии «листочки Н.Н.Константинова» из советских физ.-мат.школ
Не сразу, но довольно быстро студенты узнают, что тип 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] или что-то еще такое.
Это вопрос не про математику, а про педагогику, а я не готов в этой области спорить.
Мы находимся в ситуации, когда ни один студент за 15 лет мне ни разу не смог в начале курса дать определение понятия функции. Определение функции, которое развенчает функцию до его нормального состояния — обычного value, ни лучше, и не хуже любого иного value, таких как True, 17, (5, 'a'), [1...10] или что-то еще такое.
Может стоит начать с того для каких задач полезен/удобен этот язык?
Можете посмотреть, в каких категориях больше пакетов, и делать выводы: hackage.haskell.org/packages/
Мне тоже интересен этот вопрос. Какое у этого языка преимущество или недостатки по сравнению с другими языками программирования?
В курсе много про это сказано: преимущества, где используется, для чего.
В начале курса про это говорить не с руки. В начале курса говорится только, почему для описания функционального программирования (функциональной парадигмы, декларативного стиля) использован Haskell, а не Erlang, не Refal, не Lisp и т.д. Про это сказано в первой главе. Статья посвящена второй главе, кстати.
А вот мой вопрос знатокам промышленного программирования: какое мобильное приложение держит рекорд по числу скачиваний? И на каком языке оно написано?
В начале курса про это говорить не с руки. В начале курса говорится только, почему для описания функционального программирования (функциональной парадигмы, декларативного стиля) использован Haskell, а не Erlang, не Refal, не Lisp и т.д. Про это сказано в первой главе. Статья посвящена второй главе, кстати.
А вот мой вопрос знатокам промышленного программирования: какое мобильное приложение держит рекорд по числу скачиваний? И на каком языке оно написано?
Такое ощущение складывается, что Хаскель очень хорошо подошел бы как интерфейс какой-нибудь специализированной NoSQL. С его «ленивыми вычислениями» можно было бы творить чудеса в обработке сложных структур данных (например тот же анализ статистических данных). Или я неправ?
Прав. Как минимум, на Хаскеле достаточно широко используются NoSQL-решения: acid-state.seize.it/
Я правильно понимаю, что (x:xs) определено для массива из одного элемента и не определено для пустого массива?
С интересом почитал. Благодарю! Вас нашли сегодняшние студенты из ЯрГУ и используют Ваш конспект для своей подготовки к рубежному контролю.
/* Но, за такое определение функции ребята получат 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 в ЯрГУ).
Был бы рад, если добрые люди подкинули бы задачек для самостоятельного программирования. Если возникнет такое желание, то можно посмотреть текущую коллекцию (по ссылке и каталог «задачи») и расширить ее. Что-то из коллекции стоит выкинуть, я знаю, но количество уменьшать не хочу. Поэтому — выкину при замене на более годные.
/* Но, за такое определение функции ребята получат 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.
Конспекты лекций «Haskell как первый язык программирования». Часть1