LINUX.ORG.RU

Вышла Scala 2.10

 


1

3

Объявлено о выходе новой версии языка программирования Scala 2.10.

Основные нововведения:

  • классы-значения (value classes) — новый механизм, позволяющий уменьшить расходы на выделение памяти;
  • неявные модификаторы (implicit classes) теперь относятся к определению классов и призваны упростить расширения для других типов;
  • интерполяция строк (string interpolation) — новый механизм создания строк;
  • Futures и Promises призваны упростить создание многопоточного кода;
  • библиотека Akka Actors теперь является частью языка;
  • наконец-то в состав языка добавлена поддержка макросов.

Текущая стабильная версия языка программирования Scala может быть получена на странице загрузки проекта; исходные коды распространяются на условиях лицензии BSD.

>>> Подробности

★★★★★

Проверено: maxcom ()
Последнее исправление: mono (всего исправлений: 2)

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

Это ещё что, вот если ещё log доставать из reader и писать в ReaderT IO.

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

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

И что такое «параллельная итерация»?

Это я перепутал - наоборот последовательная, как в твоём втором примере с doseq, но параллельная, когда «параллельно» осуществляется проход по нескольким коллекциям, тоже бывает нужна (в хаскеле называется zipWith).

А этот zipWith тоже может работать с побочными эффектами?

Вот твой код

Это всё разные варианты. Нужно взять что-то одно - например класс DoSeq и его инстансы. Как выглядят эти инстансы - не интересно, понятно что последовательный doseq для нескольких коллекций будет работать как вложенные циклы - и в clojure (в реализации doseq), и в haskell (там где у меня forM_ от forM_).

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

Например, на clojure можно написать такой вариант для proof of concept (мой утрированный вариант, чисто для показа возможности)

(defmacro foreach [binds & body]
  (letfn [(make-loop [binds]
            (if (empty? binds)
              (cons 'do body)
              `(loop [col# ~(second (first binds))]
                 (when-not (empty? col#)
                  (let [~(ffirst binds) (first col#)]
                    ~(make-loop (rest binds)))
                  (recur (rest col#))))))]
    (make-loop (partition 2 binds))))

;; test

(foreach [a [1 2]
          b [:a :b]
          c ['x 'y]]
  (println a ", " b ", " c))

=> 1 ,  :a ,  x
   1 ,  :a ,  y
   1 ,  :b ,  x
   1 ,  :b ,  y
   2 ,  :a ,  x
   2 ,  :a ,  y
   2 ,  :b ,  x
   2 ,  :b ,  y

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

Ты еще приплел сюда монады и получил в конечном итоге не функции.

То что forM_ и прочие используют монадический интерфейс никак не делает их «не функциями» - они тоже обычные функции.

И поэтому для работы с ними придумали свою реализацию for, map и прочее? Не смеши меня. Ты не можешь работать с ними как с обычными функциями, т.к. результат и параметр должны быть «M _тип_».

Хосспади, какое убожество.

Это ещё что, вот если ещё log доставать из reader и писать в ReaderT IO.

Хех.

непонятное тело foreach-а

Ну там же free variable в body - wtf?

Какие free vaiables? Они забиндены в шапке. Если будет несвязанный символ, то компилятор ругнется. Так что опять мимо.

Странно, но на моей машине

Нужно тестировать оба варианта (clojure и ghc -O2) и делить.

Нафиг тестировать, мне и так все понятно. Я за то, чтобы структуры данных и алгоритмы выбирать в зависимости от задачи. Уже писал. Если нужен перформанс на больших объемах с императивными алгоритмами, то нужно писать императивно и возможно на низком уровне (джава, с++, etc).

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

Это не эндофунктор

Берём Martin-Lof category, допустим, Set в Agda. Тут важно убедиться что это категория и узнать какими свойствами она обладает (LCCC). Дальше, эндофунктор о котором идёт речь это такой рекурсивный polynomial functor:

List : Set → Set
List = λ A → ⊤ ⊎ A × List A

где ⊤, ⊎ и × это непосредственно терминальный объект и объекты суммы и произведения в категории (про которые мы уже говорили тут - всё понятно). Тут пока только отображение объектов, отображение стрелок (как map) и выполнение законов - «задание на дом», но ссылка на доказательство уже была.

Дальше, данный функтор с такой сигнатурой задаёт непосредственно «ADT» уникально (с точностью до местной эквивалентности) как W-type / initial algebra.

Наконец, так же как свойства категории Set имеют важные значения, свойства таких функторов - тоже, например, есть понятие строго-позитивных функторов (и ADT, соответственно - http://www.cs.nott.ac.uk/~txa/publ/cont-tcs.pdf), Agda все ADT которые не функторы и которые не строго-позитивны отвергает во время соответствующих проверок (в отличии от хаскеля, опять же - существование таких ADT может привести к логическим противоречиям в теории языка):

data Foo : Set where
  foo : (Foo → Foo) → Foo

-- ^ Foo is not strictly positive, because it occurs to the left of an
-- arrow in the type of the constructor foo in the definition of Foo.

-- $ agda --help
-- ...
--           --no-positivity-check                       do not warn about not strictly positive data types

Какой-нибудь Bool - тоже функтор, но уже не эндо, а из терминальной категории:

-- http://ncatlab.org/nlab/show/terminal+category
data ⊤₁ : Set₁ where
  tt : ⊤₁

Bool : ⊤₁ → Set
Bool = λ tt → ⊤ ⊎ ⊤
-- ^ ≊ Bool : Set; Bool = ⊤ ⊎ ⊤

Еще раз - открываем определение функтора

Вот и открой Пирса - начинается глава про функторы, определение функтора и первый (!) же пример - функтор List.

Вот перед тобой есть определение функтора, и есть некий объект (АДТ), который не удовлетворяет этому определению.

Отображает объекты в объекты (например, Int в List Int) и стрелки в стрелки (Int -> Int в List Int -> List Int) с выполнением всех законов - прекрасно удовлетворяет. Если есть опасения за то чтобы функтора было _достаточно_ для задания ADT - смотри по ссылкам.

Потому что семантика хаскеля так определен. Этот ответ больше устроит?

И где она так (как «так»?) определена? Что мешает доработать семантику до удобоваримой формы (так чтобы придать смысл IO)? Почему это обязательно невозможно?

Тебя забанили в гугле?

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

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

Стандартный пример из GADT (с типизацией простенького интерпретатора) можно записать в нотации индуктивных типов?

На GADTs из Haskell можно смотреть как на реализацию куска (чтобы не сказать огрызка) (ко/)индуктивных типов MLTT (M/W-types), (ко/)индуктивные типы в Coq, Epigram или Agda - тоже реализации, но уже более продвинутые в этом плане.

То есть вопрос скорее - можно ли с GADTs сделать nat indexed list type, fin type, identity types и т.п. подобные вещи - можно, но это навроде шаблонных извращений в плюсах, не предназначено оно для того. А некоторые вещи сделать совсем нельзя (более сложные зависимости типов от термов). То есть GADTs это заведомо более ограниченная вещь чем индуктивные типы вообще.

Но это ничего не меняет, потому что значки всегда можно поменять.

Ну там можно писать

data Struct = Struct {
  _foo :: ...,
  _bar :: ...,
  ...
} deriving ...

defaultStruct = Struct ...

anotherStruct = defaultStruct { _foo = ... }

update struct = struct { _bar = ... }

ещё можно представить, что есть перегрузка аксессоров и конструкторов (чего нет). Конечно, можно свести к более примитивным вещам и расписать всё явно, но насколько это получается удобно можно посмотреть на примере from/to из Generics.

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

определение функции это _и есть_ введение нового семантического правила.

Если в языке есть только абстракции и аппликации с соответствующей семантикой, то да - каждая именованная функция требует расширения семантики. Но если ввести в язык let / letrec и добавить им общие семантические правила, то уже сколько не определяй новых именованных функций через эти формы - семантика будет неизменной и статичной. Аналогично для других «простых вещей» (переменные, константы и т.п.). Сама такая (small-step) семантика предполагает вычислитель. Если оставаться консервативными, то теперь уже каждый макрос предполагает расширение исходной семантики, _до тех пор_ пока мы не пойдём дальше и не введём в язык уже определения макросов (тот же let, но уже macro-) и макро-аппликации и не дадим им семантику - это предполагает экспандер, вычислитель на разных стадиях, метацикличность. Тогда уже макросы не будут менять статичной семантики такого «языка без тормозов». Проблема в том, что «консервативное» это область известного, а «пойти дальше» - не совсем (тут опять ?).

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

А этот zipWith тоже может работать с побочными эффектами?

Есть zipWith и zipWithM, так же как map и mapM. Все четыре, как и прочие возможные итерации можно обобщить в одну перегруженную функцию doseq, либо в обычную doseq которая принимает ADT который расскажет ей какие итерации нужно совершать.

Вообще, по части обобщённости стандартные функции хаскеля довольно слабоваты (map/fmap/liftA/liftM, Monad !<= Functor, (+)/(++)/mappend/mplus ну и т.д.). В RWH это мотивировали «новичкам будет непонятно», хотя скорее всего причины чисто исторические - так сложилось.

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

До 10, 20, 50? Можно написать соответствующее количество инстансов. За применимость гетерогенных списков тут - не скажу, лучше, наверно, с ними не связываться.

Ты не можешь работать с ними как с обычными функциями, т.к. результат и параметр должны быть «M _тип_».

mapM и т.п. - обычные функции, это факт. Добыть _тип_ из M _тип_ и отдать его «ещё более обычной функции» ты всегда можешь - fmap, (>>=), поместить обратно тоже - return. Если хочешь обходить коллекции, писать в логи, читать из RealWorld, то просто пишешь соответствующий код - какие проблемы? Смущает буковка M? Если хочется делать в чистом коде IO a -> a суть которой в output - unsafePerfromIO в помощь. В input - никак, нужно думать как чистое отделить от эффектов, опять - если чистота видится как зло, то нет смысла брать хаскель, всё это было затеяно чтобы писать чистые data -> result отдельно и потом лифтить в effectful код, если мешать нетривиально одно с другим то, естественно, будет неудобно.

Какие free vaiables? Они забиндены в шапке.

И это нужно знать. Например, (foreach [a [1 2] b [3 4]] (println a ", " b ";")) может писать 1, 3; 1, 4; 2, 3; 2, 4 последовательно, а может - 1, 3; 2, 4 параллельно - нужно как-то делать выбор в этом «eDSL», типа (foreach :all-together [a [1 2] b [3 4]] (println a ", " b ";")) - идём править макрос foreach. Делать выбор в чисто функциональном API тоже надо - я говорил только про то, что макросы заведомо добавляют новое измерение в которое не хочется лезть если без него можно обойтись.

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

А этот zipWith тоже может работать с побочными эффектами?

Есть zipWith и zipWithM, так же как map и mapM. Все четыре, как и прочие возможные итерации можно обобщить в одну перегруженную функцию doseq, либо в обычную doseq которая принимает ADT который расскажет ей какие итерации нужно совершать.

Ну, ясно. Отдельная вселенная монад.

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

До 10, 20, 50? Можно написать соответствующее количество инстансов. За применимость гетерогенных списков тут - не скажу, лучше, наверно, с ними не связываться.

«написать соответствующее количество» это не решение, аналогичное решению с помощью макроса. Ну что же, засчитываем слив.

Ты не можешь работать с ними как с обычными функциями, т.к. результат и параметр должны быть «M _тип_».

mapM и т.п. - обычные функции, это факт. Добыть _тип_ из M _тип_ и отдать его «ещё более обычной функции» ты всегда можешь - fmap, (>>=), поместить обратно тоже - return. Если хочешь обходить коллекции, писать в логи, читать из RealWorld, то просто пишешь соответствующий код - какие проблемы? Смущает буковка M? Если хочется делать в чистом коде IO a -> a суть которой в output - unsafePerfromIO в помощь. В input - никак, нужно думать как чистое отделить от эффектов, опять - если чистота видится как зло, то нет смысла брать хаскель, всё это было затеяно чтобы писать чистые data -> result отдельно и потом лифтить в effectful код, если мешать нетривиально одно с другим то, естественно, будет неудобно.

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

Какие free vaiables? Они забиндены в шапке.

И это нужно знать. Например, (foreach [a [1 2] b [3 4]] (println a ", " b ";")) может писать 1, 3; 1, 4; 2, 3; 2, 4 последовательно

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

По поводу «нужно знать» не понял. Конструкция выполняет роль вложенных однопеременных foreach-ей. Т.е. это и так понятно, как это должно и будет работать. Не придумывай проблему на ровном месте.

, а может - 1, 3; 2, 4 параллельно -

Не может. Если нужно параллельно, смотри ниже.

нужно как-то делать выбор в этом «eDSL», типа (foreach :all-together [a [1 2] b [3 4]] (println a ", " b ";")) - идём править макрос foreach.

Не нужно. Решается все идиоматично, например

(doseq [[a b] (map vector [1 2] [3 4])] 
  (println a ", " b))

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

Ага, теперь ты признал необходимость макросов. Ну а как же «look ma, lambdas, closures and HOFs!»? Собственно, ЧТД. То, что ты пытаешься и здесь спустить на тормозах, тоже очевидно.

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