metaclass: (Default)
[personal profile] metaclass
Scala - это Haskell в жабьей шкуре?

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

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

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

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

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

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

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

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

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

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

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

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

Date: 2013-01-11 09:18 pm (UTC)
From: [identity profile] thesz.livejournal.com
>А Вы попробуйте 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 были не совсем подъязыками, а скорее библиотеками комбинаторов?

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

Date: 2013-01-14 01:26 am (UTC)
From: [identity profile] isorecursive.livejournal.com
@ Я не знаю, зачем нужны 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 Date: 2013-01-14 01:32 am (UTC)

Date: 2013-01-14 01:39 am (UTC)
From: [identity profile] thesz.livejournal.com
>В библиотеках комбинаторов обычно ничего такого мудрёного нету, чтоб их целесообразно было ещё какими-то 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.

Date: 2013-01-14 02:09 am (UTC)
From: [identity profile] isorecursive.livejournal.com
@ Дайте мне алгебраическую структуру и я счастлив.
Вот же:
  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), то это неприкольно, если нам хочется внутри её заиспользовать так, будто у неё её честный тип.

Date: 2013-01-14 02:20 am (UTC)
From: [identity profile] thesz.livejournal.com
>На хаскеле это будет что-то такое:
>
>{-# LANGUAGE GADTs #-}
>
>data Term :: * -> * where
>  Num :: Double -> Term Double
>  Sum :: Term Double -> Term Double -> Term Double
>  ...
>
>type (a ~> b) = Term (a -> b)
Это неверный Haskell.

> Где-нибудь можно посмотреть определение B, G, applyG?

Я предложил посмотреть на REPA и Accelerate. Там хорошо показана эта техника.

>Если получится, что любая лямбда-абстракция, которую мы хотим оттранслировать в конечный подъязык, на уровне хаскеля должна быть типа (SomeVarType -> SomeTermType), то это неприкольно, если нам хочется внутри её заиспользовать так, будто у неё её честный тип.

Я не понимаю, о чём здесь идёт речь.

Date: 2013-01-14 02:38 am (UTC)
From: [identity profile] isorecursive.livejournal.com
@ Это неверный Haskell.
Хорошо, вот:
{-# LANGUAGE GADTs, KindSignatures, TypeOperators, UnicodeSyntax #-}

import Prelude.Unicode

type (a :→ b) = Term (a → b)

data Term ∷ * → * where
  Num ∷ Double → Term Double
  Sum ∷ Term Double → Term Double → Term Double
  Var ∷ String → Term a
  Abs ∷ Term a → Term b → a :→ b
  App ∷ (a :→ b) → a → Term b

add = Sum

test ∷ (a → b) → IO ()
test = undefined

-- main = test $ \x → add x 0.5
-- Abs (Var "x") (Sum (Var "x") (Num 0.5))


Чтобы получился эквивалент того, что я продемонстрировал на Scala, нужно написать такое определение test, которое будет выводить для описанного применения main описанный вывод. Если заменить в двух закомментированных строчках x на y, предыдущее требование также должно исполняться.

@ Я предложил посмотреть на REPA и Accelerate. Там хорошо показана эта техника.
Уже начал смотреть, но пока не нашёл.

@ Я не понимаю, о чём здесь идёт речь.
Нужно, чтобы в теле лямда-абстракции, к которой апплицируется test, аргумент был доступен с типом a (см. сигнатуру test), а не с каким-нибудь HoasBinderVarType.

UPD:
Вот здесь "Если заменить в двух закомментированных строчках x на y, предыдущее требование также должно исполняться." я перестарался. С точностью до альфа-конвертируемости будет fine enought. Просто я хотел сказать, что test _ = putStr "Abs (Var "x") (Sum (Var "x") (Num 0.5))" - не подходит, но неправильно сформулировал.

UPD2: test ∷ (a :→ b) → IO ()
Edited Date: 2013-01-14 05:16 am (UTC)

Date: 2013-01-15 01:57 am (UTC)
From: [identity profile] thesz.livejournal.com
У вас снова неправильный Хаскель.

Моё решение, когда я обнаружил ошибку:
{-# LANGUAGE GADTs, KindSignatures, TypeOperators, UnicodeSyntax #-}

--import Prelude.Unicode
import Control.Monad.State

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

data Term :: * -> * where
  Num :: Double -> Term Double
  Sum :: Term Double -> Term Double -> Term Double
  Var :: String -> Term a
  Abs :: Term a -> Term b -> a :-> b
  App :: (a :-> b) -> a -> Term b

add = Sum

type MkTerm a = State Int a

class MkT a where
        mkTerm :: MkTerm (Term a)

inventVar :: MkTerm (Term a)
inventVar = do
        modify (+1)
        x <- get
        return $ Var $ "x"++show x

instance MkT b => MkT (a -> b) where
        mkTerm = do
                a <- inventVar
                b <- mkTerm
                return $ Abs a b

tyoe family ToTerm a
type instance ToTerm 

test ∷ (a -> b) -> IO ()
test = undefined

-- main = test $ \x -> add x 0.5
-- Abs (Var "x") (Sum (Var "x") (Num 0.5))
Интересно, найдёте ли вы ошибку в своём коде?

Date: 2013-01-15 11:54 pm (UTC)
From: [identity profile] isorecursive.livejournal.com
@ App :: (a :-> b) -> a -> Term b
А подразумевал
App :: (a :-> b) -> Term a -> Term b

, конечно.

@ class MkT a
@ b <- mkTerm
@ ...
Пока не представляю, что Вы хотели этим показать. С inventVar всё понятно - это такой gensym в state-монаде,
а вот mkTerm - это какой-то конструктор канонических термов для заданных типов.
Непонятно, как он поможет погружать хаскельные лямбда-абстракции в лямбда-абстракции подъязыка.
Даже пофантазировать на эту тему особо не получается, потому что ясно, что, например, для типа Double,
mkTerm будет возвращать какой-то один и тот же терм. Как, например, это соотносится с
test $ \x -> add x 0.5 -- > outputs Abs (Var "x") (Sum (Var "x") (Num 0.5))
test $ \x -> add (add x 0.5) 0.5 -- >  outputs Abs (Var "x") (Sum (Sum (Var "x") (Num 0.5)) (Num 0.5))

?
Похоже, что этот вот "MkT b => MkT (a -> b)" в "b <- mkTerm" вообще опирается исключительно на тип тела лямбда-абстракции. Как он будет погружать тело хаскельной лямбда-абстракции в подъязык, если он даже ничего о нём кроме типа не осознаёт? Для одного только типа тела Double у нас бесконечно разных термов, которые мы хотели бы погрузить в разные термы подъязыка.

Date: 2013-01-15 11:55 pm (UTC)
From: [identity profile] thesz.livejournal.com
У вас и в test неправильная сигнатура.

Date: 2013-01-16 12:58 am (UTC)
From: [identity profile] isorecursive.livejournal.com
Решить другую задачу и сказать, что постановщик ошибся в условии. Каков трюк!

Date: 2013-01-16 12:59 am (UTC)
From: [identity profile] thesz.livejournal.com
Ничем не могу помочь.

Profile

metaclass: (Default)
metaclass

April 2017

S M T W T F S
      1
2345678
9101112 131415
16171819202122
23242526272829
30      

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Sep. 14th, 2025 10:15 am
Powered by Dreamwidth Studios