metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2013-01-08 07:33 pm
Entry tags:

Знаете ли вы, что

Scala - это Haskell в жабьей шкуре?

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

Язык знатно безумный, я почти Programming in Scala дочитал. И кое-какие вещи там сильно похожи на решение некоторых проблем с наследованием и зависимостями типов друг от друга, которых мне не хватало в дельфи и дотнетах :)

[identity profile] Игорь Петров (from livejournal.com) 2013-01-09 01:18 pm (UTC)(link)
И что это за загадочные "остальные прелести языка"?

[identity profile] isorecursive.livejournal.com 2013-01-09 06:05 pm (UTC)(link)
Макросы, implicits, удобный синтаксис (согласитесь, это намного лучше, чем это).

[identity profile] kurilka.livejournal.com 2013-01-09 06:17 pm (UTC)(link)
case выглядит явно лишним, а помимо этого имхо разница очень косметическая (или речь про записать objec.method(args)?)

[identity profile] isorecursive.livejournal.com 2013-01-09 06:24 pm (UTC)(link)
Тем не менее, её хватает, чтобы в хаскеле так никто не писал, а вместо этого выдумывал имя для фунарга и выносил его в where.
Так и получается ... where f ... = ...; g ... = ... и т.д., ведь осмысленные имена не всегда придумываются, и не всегда есть время и желание думать над именованием каждого фунарга.

[identity profile] thedeemon.livejournal.com 2013-01-10 03:36 am (UTC)(link)
Часто макросы используете?

[identity profile] Игорь Петров (from livejournal.com) 2013-01-10 07:34 am (UTC)(link)
Они там появились, если я ничего не путаю, в 2.10, т.е. 4 дня назад.

[identity profile] thedeemon.livejournal.com 2013-01-10 07:53 am (UTC)(link)
Вот и мне так казалось. Хотя побаловаться с ними энтузиасты могли и до релиза.

[identity profile] isorecursive.livejournal.com 2013-01-10 07:32 pm (UTC)(link)
Я пока так и не понял, в чём суть вопроса, потому отвечать буду по наиболее вероятному предположению:
макросы нужны не только для тех вещей, которые можно было встроить в компилятор, и даже для этих, я думаю,
решение с их использованием может быть (например, если в компиляторе это всё делалось отдельными фазами, или плагинами, опирающимися на их порядок, и вынужденными думать, как не испортить при обработке предположения о коде следующей фазы и т.д.) существенно выигрышнее, чем реализация того же в компиляторе. То есть, макросы можно рассматривать и как модульные расширения компилятора, абстрагированные до уровня кода. Я слышал, что некоторые вещественные доказательства такой выигрышности уже есть, и знаю, что ещё несколько находятся в процессе разработки. Из юзкейсов макросов, которые вообще никак не могли быть учтены в хост-языке, наиболее очевидные - пользовательские оптимизации и вложения разной глубины подъязыков, всяческих логик и систем типов. Вкладываемость только на очень-очень поверхностном и стартовом уровне захватывается всякими RebindableSyntax.

[identity profile] kurilka.livejournal.com 2013-01-10 07:46 pm (UTC)(link)
Дак насколько часто-то?

[identity profile] isorecursive.livejournal.com 2013-01-10 08:12 pm (UTC)(link)
Ну вот в одном хобби-проекте вся суть в том, чтобы их заиспользовать для лучшей реализации продолжений.
В другом есть планы поэкспериментировать с развязанными модульными вложениями statement-абстракторов аля do и proc и вообще подъязыков, когда разберусь с тем, как там всё оттипизировать.

[identity profile] isorecursive.livejournal.com 2013-01-10 08:29 pm (UTC)(link)
Если точнее, по итогам последней недели, в среднем получается 1.4 макроса в день.

[identity profile] thesz.livejournal.com 2013-01-10 09:02 pm (UTC)(link)
Это означает, что вы используете язык низкого уровня.

(см. Мифический Человеко-Месяц)

[identity profile] isorecursive.livejournal.com 2013-01-10 09:15 pm (UTC)(link)
Это может означать что-угодно, потому я и пытался искать какой-то метафорический смысл в изначальном вопросе.
В данном случае это значило, что несколько макросов я написал чтобы прощупать некоторые аспекты,
а ещё пару - для проекта, который заключается в том, чтобы заменить компиляторный плагин решением на макросах.

[identity profile] thesz.livejournal.com 2013-01-10 09:18 pm (UTC)(link)
То есть, МЧМ вы не читали. Поздравляю.

[identity profile] isorecursive.livejournal.com 2013-01-10 09:29 pm (UTC)(link)
Обязательно почитаю, спасибо за совет. На всякий случай, если мне посчастливилось угадать отсылку
по описанному в вики закону: замена готового и работающего на альтернативное, но лучшее - это всё-таки другое.

[identity profile] thesz.livejournal.com 2013-01-10 09:55 pm (UTC)(link)
Нет.

Если вам требуется метапрограммирование, то ваш инструмент слишком низкого уровня.

[identity profile] isorecursive.livejournal.com 2013-01-11 07:08 pm (UTC)(link)
Если речь идёт о реализации языковой фичи, это будет в любом случае метапрограммирование, просто можно жонглировать AST в компиляторе, жестко завязываясь на его инфраструктуру и правила игры, а можно этим заниматься сбоку при помощи макросов.
Кстати, если "мета" понимать в более общем смысле, например, как в понятии "метаматематика" или "металогика", то, например, всякие паттерны вроде функторов и монад - тоже вполне метапрограммирование. Просто не символьное а структурное.

[identity profile] thesz.livejournal.com 2013-01-11 08:23 pm (UTC)(link)
Я десяток EDSL реализовал на Хаскеле, не используя макросов. Что я делаю не так?

[identity profile] isorecursive.livejournal.com 2013-01-11 09:09 pm (UTC)(link)
А Вы попробуйте http://lampwww.epfl.ch/~rompf/continuations-icfp09.pdf реализовать вне компилятора и без макросов.
Там в 3.1 наглядно показано, что можно, конечно, просто монадки звать из do/for-нотации, но это настолько неудобно, что проще будет не использовать это совсем.
А что касается EDSL, в хаскеле даже лямбда-абстракцию и аппликацию не перегрузить через RebindableSyntax, так что подъязыки с кастомными биндерами неповкладывать. Что уж говорить о кастомных подсистемах типов.
Может быть, эти EDSL были не совсем подъязыками, а скорее библиотеками комбинаторов?

[identity profile] thesz.livejournal.com 2013-01-11 09:18 pm (UTC)(link)
>А Вы попробуйте http://lampwww.epfl.ch/~rompf/continuations-icfp09.pdf реализовать вне компилятора и без макросов.

Я не знаю, зачем нужны delimited continuation. Если вы мне скажете, зачем они мне нужны, я сделаю.

> в хаскеле даже лямбда-абстракцию и аппликацию не перегрузить через RebindableSyntax,

Типы классов.

class Q a
instance Q This
instance Q That
instance (GG a, Q b) => Q (a -> b)

>Может быть, эти EDSL были не совсем подъязыками, а скорее библиотеками комбинаторов?

В чем разница?

[identity profile] isorecursive.livejournal.com 2013-01-14 01:26 am (UTC)(link)
@ Я не знаю, зачем нужны delimited continuation. Если вы мне скажете, зачем они мне нужны, я сделаю.
А Вы в раздел 4 загляните - он практическому применению посвящён.
Особенно обратите внимание на 4.2 - это как-раз то, о чём я говорил в http://metaclass.livejournal.com/763675.html?thread=15938075#t15938075

@ Типы классов.
А как они помогут пронаблюдать имя переменной лямбды и имена свободных термов? А без этого произвольный биндер подъязыка на выразишь.
Вот, смотрите:

object Sublang {
  abstract sealed class Term[T]

  type ~>[A, B] = Term[A => B]

  case class Num(x: Double)                         extends Term[Double]
  case class Sum(a: Term[Double], b: Term[Double])  extends Term[Double]
  case class Var[T](name: String)                   extends Term[T]
  case class Abs[A, B](arg: Term[A], body: Term[B]) extends (A ~> B)
  case class App[A, B](fun: A ~> B, arg: A)         extends Term[B]

  object embeddings {
    def embedLit(c: Context)(x: c.Expr[Double]): c.Expr[Term[Double]] = {
      import c.universe._
      x.tree match {
        case Ident(name) => reify { Var[Double](c.literal(name.decoded).splice) }
        case Literal(Constant(x: Double)) => reify { Num(c.literal(x).splice) }
      }
    }

    def embedVars(c: Context)(tree: c.Tree): c.Tree = {
      import c.universe.{treeCopy => tc, _}
      tree match {
        case Apply(fun, args) => tc.Apply(tree, embedVars(c)(fun), args.map{embedVars(c)(_)})
        case Ident(name)      => reify { Var(c.literal(name.decoded).splice) } tree
        case _                => tree
      }
    }

    def embedFun[A, B](c: Context)(f: c.Expr[A => Term[B]]): c.Expr[A ~> B] = {
      import c.universe._
      f.tree match { case Function(args, body) => reify {
        Abs(Var(c.literal(args.head.name.decoded).splice),
          c.Expr[Term[B]](embedVars(c)(body)).splice
        )
      }}
    }

    trait Back {
      implicit def funLifting[A, B](f: A => Term[B]): A ~> B = macro embedFun[A, B]
    }

    object Front extends Back {
      implicit def litLifting(x: Double): Term[Double] = macro embedLit
    }
  }

  def add = Sum
}


И теперь я могу:
object main extends App {
  import Sublang._
  import Sublang.embeddings.Front._

  def test[A, B](f: A ~> B) =
    println(f)

  test { x: Double =>
    add(x, 0.5)
  }

  // => Abs(Var(x),Sum(Var(x),Num(0.5)))
}


А через, например, RebindableSyntax, из этого всего получится только Num вложить.

@ В чем разница?
В библиотеках комбинаторов обычно ничего такого мудрёного нету, чтоб их целесообразно было ещё какими-то DSL, EDSL или подъязыками называть.
Если оно и так хорошо вписывается в обычные хаскельные построения, значит, это обычный хаскельный код.
Вот когда он будет не вписываться в систему типов и, например, в стратегию биндинга, и вообще выглядеть инородно (при попытке выразить без мета-уровневых средств), тогда будет смысл говорить о чём-то подобном.
Edited 2013-01-14 01:32 (UTC)

[identity profile] thesz.livejournal.com 2013-01-14 01:39 am (UTC)(link)
>В библиотеках комбинаторов обычно ничего такого мудрёного нету, чтоб их целесообразно было ещё какими-то DSL, EDSL или подъязыками называть.

Вообще-то, именно их и называют EDSL. Это синонимы.

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

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

>Вот, смотрите:

Я не могу это читать. У меня голова по-другому работает. Я не для того решил в 1998 году уйти от объектов и наследований, чтобы возвращаться обратно.

Дайте мне алгебраическую структуру и я счастлив.

>А как они помогут пронаблюдать имя переменной лямбды и имена свободных термов?

Применением.

instance (B a, G b) => G (a -> b) where
    dadam f = do
        a <- inventArg
        b <- applyG f a
        dadam b
Примерно так.

Внутри функции f будут известны её параметры, как переменные.

См. также REPA и Accelerate.

[identity profile] isorecursive.livejournal.com 2013-01-14 02:09 am (UTC)(link)
@ Дайте мне алгебраическую структуру и я счастлив.
Вот же:
  abstract sealed class Term[T]

  type ~>[A, B] = Term[A => B]

  case class Num(x: Double)                         extends Term[Double]
  case class Sum(a: Term[Double], b: Term[Double])  extends Term[Double]
  case class Var[T](name: String)                   extends Term[T]
  case class Abs[A, B](arg: Term[A], body: Term[B]) extends (A ~> B)
  case class App[A, B](fun: A ~> B, arg: A)         extends Term[B]


На хаскеле это будет что-то такое:
{-# LANGUAGE GADTs #-}

data Term :: * -> * where
  Num :: Double -> Term Double
  Sum :: Term Double -> Term Double -> Term Double
  ...

type (a ~> b) = Term (a -> b)


@ Применением.
Просто так по этому коду трудно понять, о чём речь. Где-нибудь можно посмотреть определение B, G, applyG?
Это не hoas хоть какой-нибудь? Речь же о том, чтобы иметь возможность делать произвольные биндеры, а с hoas получится, что если мы хотим какие-то другие, то нам надо сделать 2 подъязыка - первый - просто чтобы пронаблюдать переменные и оттранслировать лямбда-абстракции во второй. И ещё первый будет накладывать существенные ограничения на синтаксис подъязыка и на взаимодействие с хост-языком. Если получится, что любая лямбда-абстракция, которую мы хотим оттранслировать в конечный подъязык, на уровне хаскеля должна быть типа (SomeVarType -> SomeTermType), то это неприкольно, если нам хочется внутри её заиспользовать так, будто у неё её честный тип.

(no subject)

[identity profile] thesz.livejournal.com - 2013-01-14 02:20 (UTC) - Expand

(no subject)

[identity profile] thesz.livejournal.com - 2013-01-15 01:57 (UTC) - Expand

(no subject)

[identity profile] thesz.livejournal.com - 2013-01-15 23:55 (UTC) - Expand

(no subject)

[identity profile] thesz.livejournal.com - 2013-01-16 00:59 (UTC) - Expand

[identity profile] Игорь Петров (from livejournal.com) 2013-01-10 07:41 am (UTC)(link)
Implicits - да полезно, а про синтаксис - это шутка?
Нет, не соглашусь, синтаксис хаскеля намного лучше, с-образный синтаксис вообще для языков с фя-элементами не подходит (про полноценные фя я и не говорю).

[identity profile] isorecursive.livejournal.com 2013-01-10 07:36 pm (UTC)(link)
В этом есть доля шутки, но что касается лямбда-блоков(кроме Scala такое ещё имеется в Ruby), тут я на полном серьёзе считаю, что это большой выигрыш конкретно для ФП блокового синтаксиса по сравнению с отступным. В хаскеле, конечно, добавили недавно LambdaCase, но оно реабилитирует только часть преимуществ, которая не связана непосредственно с блоковостью.