LINUX.ORG.RU

Снова макросы Racket

 ,


1

2

Пытаюсь использовать макрос

(define-syntax-rule (with-template ([var ...] [form ...] ...) body ...)
  (begin (define-syntax-rule (inner var ...) (begin body ...))
         (inner form ...) ...))

для описания пачки однотипных макросов

(with-template 
 ([src dst]
  [define-gi define-gi*]
  [define-gtk define-gtk*])
(define-syntax (dst stx)
  (syntax-case stx ()
    [(dst id params ...)
     (let ([new-id (string->symbol (string-replace (symbol->string (syntax-e #'id)) "-" "_"))])
       #`(src id params ... #:c-id #,new-id))])))

Получаю очень странную ошибку main.rkt:38:20: syntax: no pattern variables before ellipsis in template at: ... in: (begin (define...syntax-e (syntax id))) "-" «_»)))) (quasisyntax (src id params ... #:c-id (unsyntax new-id))))))))

При том, что

(define-syntax (define-gtk* stx)
  (syntax-case stx ()
    [(define-gtk* id params ...)
     (let ([new-id (string->symbol (string-replace (symbol->string (syntax-e #'id)) "-" "_"))])
       #`(define-gtk id params ... #:c-id #,new-id))]))
работает прекрасно

Что я ещё не понял про рэкетовские макросы?

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

Моя ошибка: пропустил (require (for-syntax ...))

Да. Этот вариант работает замечательно. Шаблон, конечно, страшненький получается. Через defaults было бы красивее, но, похоже, невозможно

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

В #:с-id вместо первого позиционного аргумента p1 попадает.

Хм, другие варианты у меня работали, а этот нет.

Если мне нужна функция и в for-syntax и в фазе 0

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

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

В #:с-id вместо первого позиционного аргумента p1 попадает.

А вот это, видимо, баг. Но если уж все равно используем template, то можно сделать так:

(define-syntax (dst stx)
  (syntax-parse stx
    [(_ (~or (~optional (~seq #:c-id c-id:id))
             (~seq (~and k:keyword (~not #:c-id)) e:expr)
             (~once p1:expr)
             p:expr) ...)
     #:when (identifier? #'p1)
     (template (src p1 p ... (?@ k e) ... #:c-id (?? c-id p1)))]))

Нет аналога Common Lisp'ового (eval-when (:execute :load-toplevel :compile-toplevel) ...) ?

Нет, слава богу, есть фазы, что избавляет от этой жути :)

Если мне нужна функция и в for-syntax и в фазе 0, мне только дважды копировать тело функции: define + define-for-syntax ?

(require module (for-syntax module)). можете сделать вложенный модуль и его же require:

(module test racket
  (provide foo)
  (define (foo x) (displayln x)))

(require 'test
         (for-syntax 'test))

(foo 2)
(begin-for-syntax (foo 1)) 

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

Если по мелочи:

  • везде #lang racket (racket/base + require доп. библиотек)
  • (- x 1) -> (sub1 x)
  • именованный let в принципе не признаешь? (let loop - идиоматично и читабельно ☺)
  • let для внутренних определений (хоть и обычное дело, но c недавних пор в racket можно (и лучше) define)
  • если приходится в явном виде по списку бегать (например make-out), удобнее использовать match вместо car & cdr
  • местами криво выровнено, бросается в глаза

Исходник main.rkt в нечитаемом виде.

Ну а по сути ничего не скажу, с gtk не имел дела.

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

везде #lang racket (racket/base + require доп. библиотек)

А зачем тогда lang racket?

(- x 1) -> (sub1 x)

Вычищал. Видимо, не везде

именованный let в принципе не признаешь? (let loop - идиоматично и читабельно ☺)

В cl такого не было. В r5rs вроде тоже. Не привык.

let для внутренних определений (хоть и обычное дело, но c недавних пор в racket можно (и лучше) define)

А не работает.

(if info 
    (begin
      (define ...)
      ...)
     #f)

синтаксическую ошибку показывает. Ещё пара мест let-однострочники, чтобы было видно, что переменная для конкретной строки. А в остальных местах там везде define.

по списку бегать (например make-out), удобнее использовать match вместо car & cdr

Ну тогда уж сразу for/list. Как-то match даже в родных исходниках очень неактивно используется.

;;; ffi/unsafe :: define _enum
  (let loop ([i 0] [symbols symbols])
    (unless (null? symbols)
      (let-values ([(i rest) (if (and (pair? (cdr symbols))
                                      (eq? '= (cadr symbols))
                                      (pair? (cddr symbols)))
                               (values (caddr symbols) (cdddr symbols))
                               (values i (cdr symbols)))])
        (set! sym->int (cons (cons (car symbols) i) sym->int))
        (set! int->sym (cons (cons i (car symbols)) int->sym))
        (loop (add1 i) rest))))

местами криво выровнено, бросается в глаза

Всё автоматически. DrRacket виноват. А если серъёзно, то где поправить?

Исходник main.rkt в нечитаемом виде.

Опять DrRacket. Иногда нормально сохраняет. Иногда в таком виде. Читает всегда нормально. Похоже, какой-то его внутренний формат ------

Ещё вопрос: как сделать такое

(define (func param)
  (unless (check param)
    (return #f))
  ;;; далее много строк обработки param
)

В смысле, что здесь вместо return?

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

А зачем тогда lang racket?

Для случаев когда нужно «грязно и быстро». Например, скрипт какой по-быстрому набросать и не парится с require. Но для библиотек точно так делать не стоит.

А не работает.

Да так не будет работать. Вариантов несколько:

(if info 
    (let ()
      (define ...)
      ...)
     #f)
(require racket/block)
(if info 
    (block
      (define ...)
      ...)
     #f)
(cond
  [info 
   (define ...)
   ...])
  [else #f])
На практике обычно используют 1 и 3. А второй очень редко видел, только где-нибудь в тестах. Не жалуют.

Всё автоматически. DrRacket виноват. А если серъёзно, то где поправить?

Ну про «местами» я переборщил. Повторный осмотр выявил только object.rkt, find-method.

Ну тогда уж сразу for/list. Как-то match даже в родных исходниках очень неактивно используется.

Я имел в виду «явно» бегать. Если для случая достаточно форм for/ то, конечно их и стоит брать.

(let loop ([acc null]
           [lst lst])
  (match lst
    ['()
     (reverse acc)]
    [(cons lst-first lst-rest)
     (loop (cons lst-first acc)
           lst-rest)]))
В родных исходниках многое просто не хотят переписывать, чтобы не сломать. А в racket2, судя по wishlist, хотят «Make every place where binding variables currently appear allow arbitrary match patterns», потому, что удобно очень.

Опять DrRacket. Иногда нормально сохраняет.

Такая фигня, когда вставляешь какой-нибудь XML Box.

Ещё вопрос: как сделать такое

(define (func param)
  (let/ec return-k
   (unless (check param)
    (return-k #f))
    ;;; далее много строк обработки param)
    ))
qaqa ★★
()
Ответ на: комментарий от qaqa

ну а вообще так на racket не пишут конечно

А как идеологически верно пишут проверки? Просто даже на Си кроме goto end ничего придумать не смогли. Или в таких случаях всюду raise втыкать, а если наружу исключение не надо, то в with-handlers заворачивать?

Или как идеологически верно переписать то, что на CL выглядит так:

(defun seq-processed (seq)
  "If found -- run, return t. if no partial found, return t, both means
sequence processed"
  (let ((res t))
    (mapc (lambda (x)
            (let ((l (length seq)) (lx (length (car x))))
              (cond 
                ((eql l lx)
                 (when (equal (car x) seq)
                 (progn (funcall (cdr x)) (return-from seq-processed t))))
                ((> lx l)
                 (if (equal (subseq (car x) 0 l) seq)
                   (setf res nil))))))
          *global-keymap*)
    res))

то есть буквальный перевод даёт

(define (seq-processed seq)
  "If found -- run, return t. if no partial found, return t, both means
sequence processed"
  (let/ec return-k
    (let ([res #t])
      (map (lambda (x)
             (let [(l (length seq)) 
                   (lx (length (car x)))]
               (cond 
                 [(= l lx)
                  (when (equal? (car x) seq)
                    ((cdr x)) 
                    (return-k #t))]
                 [(> lx l)
                  (when (equal? (take (car x) l) seq)
                    (set! res #f))])))
          *global-keymap*)
    res)))

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

Ещё вопрос по стилю. Как лучше

(define (find-method info name)
  (cond 
    ([info
      (or (g-object-info-find-method info name)
          (find-method (g-object-info-get-parent info) name))])
    (else #f)))

или

(define (find-method info name)
  (and info
       (or (g-object-info-find-method info name)
           (find-method (g-object-info-get-parent info) name))))

?

Был бы when как в CL, вопросов бы не было, но в Racket он не возвращает #f в случае, когда ложь.

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

А как идеологически верно пишут проверки?

(if (pred? x) *здесь выходим из функции* *тут код*)

Или в таких случаях всюду raise втыкать, а если наружу исключение не надо, то в with-handlers заворачивать?

Если нужно (то есть не получается без затрат переписать по-другому), то вариант с (let/ec return-from-* *код*) - стандартное решение.

Или как идеологически верно переписать то, что на CL выглядит так:

Ну это как раз тот случий, когда лучше сделать let/ec return чем ломать мозг себе:

(define (seq-processed seq)
  (let/ec return
    (define l (length seq))
    (for*/fold ([res #t]) 
      ([(k v) (in-dict *global-keymap*)]
       [lx (length k)]
       #:unless (> lx l))
    (cond
      [(= l lx) 
       (when (equal? k seq) 
         (v) (return #t))]
      [(> lx l)
       (not (equal? (take k l) seq))]))))

anonymous
()
Ответ на: комментарий от monk
синтаксическую ошибку показывает.

делай cond вместо if

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

В лоб переписывается как-то так:

(define (seq-processed seq)
  ;; If found -- run, return #t.
  ;; if no partial found, return #t, both means sequence processed
  (define seq-length (length seq))
  (let loop ([lst *global-key-map*]
             [res #t])
    (match lst
      ['() res]
      [(cons lst-first lst-rest)
       (define lst-first-length (length lst-first))
       (cond
         [(seq-length . = . lst-first-length)
          (if (equal? (car lst-first) seq)
              (begin
                ((cdr lst-first))
                (loop lst-rest res))
              #t)]
         [(seq-length . > .lst-first-length)
          (if (equal? (take (car lst-first) seq-length)
                      seq)
              (loop lst-rest #f)
              (loop lst-rest res))]
         )])))
Но я контекста не знаю, поэтому названия переменных безмысленные. Надо нормально назвать. Ну и подумать может как по-другому написать. Но, если лень, можно и let/ec, только в таких случаях он не оправдан. Без него не обойтись в случае обхода какого нибудь дерева или графа, когда древовидная рекурсия, и надо «выпрыгнуть» без возврата из нескольких вложенных вызовов.

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

(if (pred? x) *здесь выходим из функции* *тут код*)

Основная проблема: добавляет уровень вложенности

(define (process x)
  (if (not (check0 x)) #f
    (let ([x1 (process x)])
       (if (not (check1 x1)) #f
          (let ([x2 (process x1)])
             (if (not (check2 x2)) #f x2))))))

Читается как-то не очень... Даже если без let

(define (process x)
  (cond 
    [(not (check0 x)) (warn "check0") #f]
    [else 
       (define x1 (process x))
       (cond 
         [(not (check1 x1)) (warn "check1") #f]
         [else 
           (define x1 (process x))
           (if (not (check2 x2)) 
             (begin (warn "check2") #f) 
             x2)])]))

А проверок может быть и больше (обычно должно быть больше).

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

Во-первых, прочитайте вот это (если еще не читали):

http://www.ccs.neu.edu/home/matthias/Style/style/index.html

Огромное спасибо.

Кстати, а нету такого же но по документированию программ? http://docs.racket-lang.org/scribble/plt-manuals.html читал, но там уже про внешнее API и его использование. А хочется что-то типа такого: https://github.com/Kalimehtar/cffi-objects/blob/master/freeable.lisp Результат: https://github.com/Kalimehtar/cffi-objects/blob/master/cffi-objects.pdf?raw=true

Или только комментарии в коде можно?

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

Напиши макрос!

Так ведь он также будет добавлять уровень вложенности

(define (process x)
  (with-check check0 x
    (define x1 (process1 x))
    (with-check check1 x1
      (define x2 (process2 x))
      ....)))

Или какой синтаксис у макроса предполагаешь?

По документации, похоже этот вариант предполагает всё-таки экспешены. Типа

(define (process x)
  (with-handlers [(make-exn:fail:user? (lambda (e) #f)]                                  
    (unless (check0 x) (raise-user-error 'check0))
    (define x1 (process1 x))
    (unless (check1 x1) (raise-user-error 'check1))
    (define x2 (process2 x1))
    (unless (check2 x2) (raise-user-error 'check2))
    x2))

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

Как лучше делать не подскажу. Ты хочешь писать как на Си, тоесть если что не так - возвращать #f и warning в отладочную печать. Но если в виде макроса, то как-то так:

;;#lang racket/base

(require (for-syntax racket/base syntax/parse))

(define-syntax (m stx)
  (syntax-parse stx
    [(_ (e:expr ... (~or (~and (~seq #:warn str:str) (~parse show-warn #'(displayln str)))
                         (~and (~seq) (~parse show-warn #'(void)))))
        ...)
     #'(cond
         [(let () e ...)
          (begin show-warn #f)]
         ...)]))

(m [(define x 1)
    (> x 0) #:warn "msg1"]
   [#t #:warn "msg2"]
   [#f 'do-something-else])
Вызов displayln заменить на warn.

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

А корректнее:

(define-syntax (m stx)
  (syntax-parse stx
    [(_ (e:expr ...+ (~or (~and (~seq #:warn str:str) (~parse show-warn #'(displayln str)))
                         (~and (~seq) (~parse show-warn #'(void)))))
        ...
        ((~literal default) de:expr ...+))
     #'(cond
         [(let () e ...)
          (begin show-warn #f)]
         ...
         [else
          de ...])
     ]))

(m [(void) #:warn "msg1"]
   [#t #:warn "msg2"]
   [#f 'do-something-else]
   [default (display 'ok)])

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

Ты хочешь писать как на Си, тоесть если что не так - возвращать #f и warning в отладочную печать.

Не в отладочную печать, а в протокол ошибок, но по сути так.

Ты хочешь писать как на Си

В Racket принято как-то по-другому? В CL для моего варианта есть return-from и он достаточно активно применяется. В Racket есть let/ec, но и от тебя и от anonymous я слышал, что «так не делают». Вот и интересно, а как же делают?

Вот типовая задача: чтение переменной из конфига. Нужно проверить, что есть файл, что в нём есть нужная переменная, что у неё допустимое значение. Причём любая проверка делает дальнейшую работу функции бессмысленной (нет смысла искать переменную в несуществующем файле), но проверка не должна рушить программу (так как если значения нет неважно почему, то должно использоваться какое-то умолчание, а не падать программа).

monk ★★★★★
() автор топика
Ответ на: комментарий от monk
(and (file-exists? path-to-file)
     (let ([parsed (parse-cfg path-to-file)])
       (and parsed
            (find-var-value 'var parsed))))

Не так уж и много уровней вложенности. Но в случае, если действительно много, можно и макросом.

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

Не так уж и много уровней вложенности

Как минимум, пропущено

(and (file-exists? path-to-file)
     (let ([parsed (parse-cfg path-to-file)])
       (and parsed
            (let ([value (find-var-value 'var parsed)])
                 (and (check-value value) value)))))

Выше было указано, что заменять cond через and не очень принято. Я так понимаю, в такой ситуации нормальный программист на Racket просто добавит промежуточные функции (типа parse/find-value). Так? Или есть другие методы борьбы со вложенностью от проверок?

Радикальный путь выглядит как

  (define parsed null)
  (define value null)
  (and 
    (check-file path-to-file)
    (begin (set! parsed (parse-cfg path-to-file)) parsed)
    (begin (set! value (find-var-value 'var parsed)) value)
    (check-value value)
    value)
но мне кажется «так тоже не принято»... Или нормально?

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

Как минимум, пропущено

ну и все равно нормально! В чем проблема? :)

Радикальный путь выглядит как

В общем, мое мнение - делай пока тупо через and. Ну или макрос напиши для сахарку.

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

Типа анафорический and

Вот накидал по-быстрому:

;;#lang racket/base

(require (for-syntax racket/base syntax/parse) racket/stxparam)

(define-syntax-parameter @ (lambda (stx) (raise-syntax-error #f "wrong usage!" stx)))

(define-syntax (aand stx)
  (define (helper stx)
    (syntax-parse stx
      [((e:expr ... le:expr) . rest)
       (if (null? (syntax->list #'rest))
           #'(let () e ... le)
           #`(let () e ... (let ([r le])
                             (syntax-parameterize ([@ (make-rename-transformer #'r)])
                               (and r #,(helper #'rest))))))]))
  (syntax-parse stx
    [(_ (~and (~seq (e:expr ...+))
              (~seq body))
        ...)
     #`(and #,(helper #'(body ...)))]))

(aand [(file-exists? path-to-file)]
      [(parse-cfg path-to-file)]
      [(find-value 'varname @)]
      [(check-value @)])

@ - это результат предыдущей стадии.

qaqa ★★
()
Ответ на: Типа анафорический and от qaqa

Ещё можно от прописывания параметров избавиться — в хаскеле есть такие Клейсли-стрелки, с комбинаторами для них получается так:

checkFile >=> parse >=> (findVarValue "a" >=> checkValue) >>> up
--                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--                      ^ pure part

(функции могут работать в произвольных трансформерах — MaybeT если нужно просто протаскивать нейтральное значение как and, или что-то более сложное можно придумать, с накоплением отчёта об ошибках, например).

Так что (run (batch file-exists? parse-cfg (find-value 'varname) check-value) path). Но специфично, конечно.

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

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

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

Театр двух актеров, лол.

Театр - это нормально. Цирк - было бы печальнее...

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

checkFile >=> parse >=> (findVarValue «a» >=> checkValue)

(run (batch file-exists? parse-cfg (find-value 'varname) check-value) path)

Красиво. Только aand выше очевиднее. В этом коде я не вижу, как определяется, что в parse-cfg попадает снова path, а в (find-value 'varname) результат parse-cfg ? Или здесь ошибка в алгоритме?

monk ★★★★★
() автор топика
Ответ на: Типа анафорический and от qaqa

Наверное стоит упростить, до одного выражения на стадии. Для более сложных случаев, можно явно let использовать. Так и макрос попроще, и скобок меньше.

;;#lang racket/base

(require (for-syntax racket/base syntax/parse) racket/stxparam)

(define-syntax-parameter @ (lambda (stx) (raise-syntax-error #f "wrong usage!" stx)))

(define-syntax (aand stx)
  (define (helper stx)
    (syntax-parse stx
      [(e:expr . rest)
       (if (null? (syntax->list #'rest))
           #'e
           #`(let ([r e])
               (syntax-parameterize ([@ (make-rename-transformer #'r)])
                 (and r #,(helper #'rest)))))]))
  (syntax-parse stx
    [(_ e:expr ...+)
     #`(and #,(helper #'(e ...)))]))

(aand (file-exists? path-to-file)
      (parse-cfg path-to-file)
      (find-value 'varname @)
      (check-value @))

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

(>=>) берёт по «унарной» функции слева и справа и возвращает новую «унарную» функцию (кавычки, так как m x может быть функциональным типом):

(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)

сравним с обычной композицией функций:

(.) :: (b -> c) -> (a -> b) -> (a -> c)

то есть flip (>=>) == (<=<) ~~ (.). ~~ означает эквивалентность в случае identity monad — ((c <=< b <=< a) arg) и ((c . b . a) arg) будут одинаково вычислять всю цепочку (c (b (a arg))), у нас задача её прервать, то есть вставить проверок в каждый узел, это получается переходом к kleisli category вокруг другой монады — MaybeT IO, например.

То есть (>=>) это обобщение композиции функций.

Эти три функции:

checkFile :: FilePath -> MaybeIO FilePath
parse :: FilePath -> MaybeIO Map
checkValue :: Value -> Maybe Value

уже унарны, поэтому просто передаются. Бинарная

findVarValue :: Key -> Map -> Maybe Value

делается унарной частичным применением

findVarValue "a" :: Map -> Maybe Value

то что выходит из стрелки слева сочетается по типу со входом вправо (FilePath/FilePath, например), так же как и по монадическому типу (MaybeIO — слой с эффектами, Maybe — обычный чистый, приходится их явно смешать с помощью up).

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

Нашёл. Называется «читайте маны, они рулез». http://docs.racket-lang.org/srfi-std/srfi-2.html

(require srfi/2)

(and-let* (((file-exists? path-to-file))
           (parsed (parse-cfg path-to-file))
           (value (find-value 'varname parsed))
           (check-value value))
  value)

Всё красиво, единообразно и согласно стандарта :-)))

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

checkFile :: FilePath -> MaybeIO FilePath

Теперь понял. У меня в контексте лиспа предполагалось, что возвращается булево, поэтому и не мог понять, как ты аргумент первой функции ко второй перебросил.

Дальше очевидно.

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

Всё красиво, единообразно и согласно стандарта :-)))

Это больше к scheme применимо, а racket уже не sсheme :) Но, если устраивает - почему бы и нет.

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

Можно взять обычные

checkValue :: Value -> Bool
checkValue v = ...

checkFile :: FilePath -> IO Bool
checkFile = doesFileExist

и подмешивать к стрелкам в виде фильтров другими комбинаторами:

(|>>>) :: (Monad m, MonadPlus (t m), MonadTrans t) => (a -> m Bool) -> (a -> t m b) -> a -> t m b
(f |>>> g) x = lift (f x) >>= choose (g x) mzero

(>^|) :: MonadPlus m => (a -> m b) -> (b -> Bool) -> a -> m b
(f >^| g) x = do { r <- f x; choose (return r) mzero (g r) }

t :: FilePath -> MaybeIO Value
t = checkFile |>>> parse >=> findVarValue "a" >^| checkValue >>> up

главное потом не запутаться во всём этом добре :)

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

главное потом не запутаться во всём этом добре

Это точно. Фактически, сейчас ты нарисовал _разные_ стрелочки для _конкретного_ алгоритма. Некрасиво.

А в хаскеле можно на шагах стрелочек переменные определять?

Типа

value = checkFile Path 
|>> Parsed = parseCfg Path 
|>> value = findValue Parsed "valname"
|>> checkValue value
|>> value

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

Как ты угадал — у GHC есть -XArrows:

t = proc path -> do
    checkFile -< path
    parsed <- parse -< path
    val <- findVarValue "a" -< parsed
    checkValue -< val
    returnA -< val
quasimoto ★★★★
()
Ответ на: комментарий от monk

Макрос типа do-нотации для maybe монады подойдет.

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

стрелки/монады и т.п. в данном случае - очень жуткий оверхед. Вообще говоря они в любом случае - жуткий оверхе.

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

Вообще говоря они в любом случае - жуткий оверхе.

Для чистого ленивого языка с хорошим оптимизатором — не факт.

Также как продолжения в CL — всегда жуткий оверхед, а для Racket или Smalltalk — вполне нормально.

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

Для чистого ленивого языка с хорошим оптимизатором — не факт.

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

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

Интерфейс у этих стрелок абстрактный и потому бесполезный

А как ещё в чистом языке указать порядок действий кроме монад/стрелок/do-нотации?

Явной передачей дополнительного параметра? Так выглядеть ещё страшнее будет.

XMonad вполне симпатично (в коде) выглядит. Хотя интерфейс — монада.

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

А как ещё в чистом языке указать порядок действий кроме монад/стрелок/do-нотации?

Да как угодно. Но это будет конкретный интерфейс, а не абстрактный. Как и для монады IO конкретный. Или для монады Maybe. А вот абстрактный монадический интерфейс (для монады в общем) - бесполезен абсолютно. По-этому можно все монады убрать нафиг, а каждую конкретную допилить для лучшего соответствия домену.

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

И всё-таки (<$>) может быть предпочтительней чем mapIO, mapMaybe, mapEither, mapList, mapZipList, mapTree, mapFunction, mapTuple, mapArray и т.п. Равно как и другие функции других абстрактных интерфейсов (которые помимо хаскеля используются в CL, C++, Rust, Java, Scala, как минимум).

Лишние телодвижения могут быть от newtype-ов — поднять существующие функции в трансформер, а потом ещё в стрелку, например — по два знака (минимум) на каждый вызов, или обвёртки, плюс трансформер и стрелку написать, если существующие не подходят.

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

И всё-таки (<$>) может быть предпочтительней чем

Нет. Ни одного случая в котором он более предпочтителен - нет.

Лишние телодвижения могут быть от newtype-ов

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

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

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

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

Ещё можно от прописывания параметров избавиться

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

Наверное я неправильно понял суть «избавления от прописывания параметров». Наверное имелось в виду что-то вроде этого:

The ~> Threading Macro, Syntax Parameters and a Threading Macro ?

пример:

(~> (~> (list 1 2 3)
        (map add1 <>))
    (~> <>
        (map add1 <>)
        (map + (<> 2) <>))
    list->vector)

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