LINUX.ORG.RU

[F#] apply


0

2

помогите понять почему это не верно? на сколько я знаю, карринг вполне позволяет ф-ции возвращать результаты разных типов, в зависимости от количества переданных аргументов. К примеру ф-ция (A -> B -> C) может возвращать либо (B -> C) либо C

exception MultiArgsError of string 
let rec apply f args = 
    match args with 
    | arg :: [] -> f arg
    | arg :: args ->
        apply (f arg) args        
    | [] -> raise (MultiArgsError("args count incorrect"))

Program.fs(25,16): error FS0001: Type mismatch. Expecting a
    'a -> 'b    
but given a
    'b    
The resulting type would be infinite when unifying ''a' and ''b -> 'a'

К примеру ф-ция (A -> B -> C) может возвращать либо (B -> C) либо C

В F# любая функция может возвращать только строго определённый 'a. Т.е. либо только B -> C, либо только C в твоём случае.

Norgat ★★★★★
()

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

dave ★★★★★
()
Ответ на: комментарий от Norgat
let f a b = a + b
let r1 = f 1 // 'b -> int 
let r2 = f 1 2 // int

здесь она вполне естественно, карринг же, вернула два разных типа, которые впоследствии можно использовать.

pseudo-cat ★★★
() автор топика
Ответ на: комментарий от dave

ну вот, к примеру, кусок моего internal DSL:

check Targets.Principle on 
    (haveElement  
        [(property "Имя" withValue "Диод" ofType MatchType.Text);
         (property "Полярность" withValue "Би.*" ofType MatchType.Regex)])
от лишних скобок можно избавиться, если передавать отдельно ф-цию haveElement и список её аргументов(а было бы что нибудь наподобие &rest-параметров, то вообще бы красота была). То, что добавятся квадратные скобки это херня - легче объяснить одну операцию UNION над множествами, чем рассказывать про лево/правоассоциативные порядки и приоритеты операторов

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

Все равно не понял. Может быть, стоит попробовать выразить через функции высшего порядка? Например, через map или reduce.

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

ну к примеру человек, не знакомый с программированием, хочет написать:

проверить схему на наличие 
    [элемента1 с [параметр1, параметр2], элемента2 с [параметр3], элемента3 с []] 
так как у нас левоассоциативный порядок вычисления, то ф-ция «наличие» берётся как аргумент ф-ции «проверить», а надо чтобы «наличие» выполнялось над «[элемента1 с [параметр1, параметр2], элемента2 с [параметр3], элемента3 с []]]» и результат выполнения уже был аргументом «проверить».

Если есть apply, можно изменить «наличие» из формы:

схема -> на -> (схема -> bool)
в форму
схема -> на -> (схема -> аргументы -> (схема -> bool)) аргументы

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

Ты не понял о чём я говорил.

Во-первых, каждое выражение имеет тип, причём этот тип должен быть либо чётко прописан руками, либо выводится компилятором.

Т.е. каждый x в let имеет свой тип T, который выводится руками или компилятором: let x: T = expr, т.е. expr должен возвращать объект типа T.

let f a b = a + b
let r1 = f 1 // 'b -> int 
let r2 = f 1 2 // int

Тут всё хорошо потому что каждый r имеет свой собственный тип.

То, что ты хочешь делается примерно так:


let foo f = 
    fun b -> f b


type T<'a,'b> =
    | T1 of ('a -> 'b)
    | T2 of 'b


let rec app f' a = 
    match a with
        | (None, b) -> T1 <| foo f'
        | (f, b) ->       T2 <| foo f' b

Получаем типы:

val foo : ('a -> 'b) -> 'a -> 'b
type T<'a,'b> =
  | T1 of ('a -> 'b)
  | T2 of 'b
val app : ('a -> 'b) -> 'c option * 'a -> T<'a,'b>
Norgat ★★★★★
()
Ответ на: комментарий от pseudo-cat

Тебе нужно переставить порядок аргументов?

> let rec apply scheme f args = f scheme args;;

val apply : 'a -> ('a -> 'b -> 'c) -> 'b -> 'c
dave ★★★★★
()
Ответ на: комментарий от dave

let f a b = a + b;;

val f : int -> int -> int

apply 1 f 2;;

val it : int = 3

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

нет. Вот, к примеру здесь в секции Function Values есть пару примеров. НО это опять же не походит, т.к. там фиксированное кол-во аргументов у apply и писать для каждого кол-ва аргументов свой apply не катит, так же как и писать все ф-ции, использующие apply со своим кол-ом аргументов.

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

что-то я не понимаю как с помощью этого написать, к примеру, вот такое:

Так, давай ты опишешь то, что должна делать apply словами. Очень подробно. Потому что либо мы тут не понимаем тебя, либо тебе нужна рефлексия.

Norgat ★★★★★
()
Ответ на: комментарий от pseudo-cat

Вот, к примеру здесь в секции Function Values есть пару примеров.

Подозреваю, что тебе проще захардкодить всё это безобразие в плане применения в f.

т.к. там фиксированное кол-во аргументов у apply и писать для каждого кол-ва аргументов свой apply не катит

http://msdn.microsoft.com/ru-ru/library/dd233229.aspx -> [<ParamArray>]

Но придётся использовать скобочки при вызове apply.

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

ок, вот желаемый пример работы:

let f1 a b = a + b
let f2 a b c d = a - b + c + d
apply f1 2 3 // -> 5
apply f2 1 2 3 4  // -> 6
словами - нужно вычислять произвольные ф-ции, желательно возвращающие отличные типы результатов, с разным кол-вом аргументов над списком этих аргументов.

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

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

Что должно происходить в случае, если аргументов в apply недостаточно для вычисления функции?

Norgat ★★★★★
()
Ответ на: комментарий от pseudo-cat

можно raise

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

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

Как бы трабл в том, что я хз, как вытащить кол-во агрументов у функции. Может быть там всё гораздо проще (скорее всего так и есть, и где-нибудь в GetType() валяется кол-во и тип параметров).

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

костылёк вроде намечается, интересно только что со сложными типами будет при повышении иерархии от obj до такого типа

let sum ([a;b;c] : obj list) = 
    let a : int = a :?> int        
    let b : int = b :?> int       
    (a + b), c

> sum [1;2; "a"];;
val it : int * obj = (3, "a")
> let apply f (args : obj list) = f args;;

val apply : (obj list -> 'a) -> obj list -> 'a

> apply sum [1;2;"a"];;
val it : int * obj = (3, "a")

pseudo-cat ★★★
() автор топика
Ответ на: комментарий от Norgat

и будет C# + F# + IronPython, и то последний только из-за желания убрать пару скобок... Мне проще было бы это на лиспе писать, если честно

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

Я что-то сомневаюсь, что динамические funcall или apply можно реализовать в таком строго-, очень строго-типизированном F#.

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

ну вот костыль вроде тестирую уже, правда меня уже обозвали питоноводом) Хотя конечно это всё жутко не красиво, но как объяснить не программисту, зачем нужны эти «лишние» скобки...

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

Немного наврал. Funcall уже есть. Это (<|). А вот на счет лисповского apply я очень сильно сомневаюсь. Ужасно динамическая вещь. Не для статики.

dave ★★★★★
()
Ответ на: комментарий от pseudo-cat

На всякий случай:

> let apply = (<|);;

val apply : (('a -> 'b) -> 'a -> 'b)

> let f1 a b = a + b
- let f2 a b c d = a - b + c + d;;

val f1 : int -> int -> int
val f2 : int -> int -> int -> int -> int

> apply f1 2 3;;
val it : int = 5
> apply f2 1 2 3 4;;
val it : int = 6
dave ★★★★★
()
Ответ на: комментарий от dave

кстати, может и получится что нибудь, завтра подумаю. Можно попытаться сделать конвеер из ф-ции и apply, имитируя тем самым rest-параметры у ф-ции

pseudo-cat ★★★
() автор топика
Ответ на: комментарий от pseudo-cat
let f1 a b = a + b
let f2 a b c d = a - b + c + d
apply f1 2 3 // -> 5
apply f2 1 2 3 4  // -> 6

что-то я не понял, зачем тут apply может ты имеешь в виду что-то вроде

let f1 a b = a + b
let f2 a b c d = a - b + c + d
apply f1 [2, 3] // -> 5
apply f2 [1, 2, 3, 4]  // -> 6
?

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

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

pseudo-cat ★★★
() автор топика
Ответ на: комментарий от ram

Он, по-моему, хочет передавать список, не заключая его в скобки.

И чем это будет отличаться от просто применения функции?

korvin_ ★★★★★
()
Ответ на: комментарий от pseudo-cat

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

И как функция должна определить, что уже все переданные аргументы приняты и выполнить собственно тело, а не примениться частично и вернуть еще одну каррированную функцию, ожидающую еще неизвестное количество аргументов?

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

А, впрочем можно допустить, что сколько бы аргументов не было передано в &rest, это уже «последние» аргументы. Точнее последний перед &rest аргумент является последним (простите), а в &rest попадает список дополнительных аргументов.

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

Ну я не знаком с системой типов F#, это уж ТС пусть разбирается, мне лишь интересно, как он вообще предлагает совместить неопределенное количество параметров и автоматическое каррирование + частичное применение.

korvin_ ★★★★★
()

Блин. Вот зачем было распиаривать функциональные языки так? Теперь полезут кто ни попадя писать на F# как на фортране. Или лиспе, хе-хе.

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

тебе пример кода кажется неестественным, я про internal DSL, или может, хочешь сказать, что это в принципе невозможно на F#?

pseudo-cat ★★★
() автор топика

Я же не настаиваю на подходе лиспа с его &rest, просто есть вполне жизненная задача, все решения которой, найденные в этом топике, выглядит как костыль. К тому же и саму задачу вы уже считаете еретиковой и не достойной реализации на кошерном F# :)

Если есть предложения по-другому решить, милости просим, а пока что у меня сложились три пути её решения:

  • obj list в качестве типа аргумента ф-ции, что не кошерно
  • apply f от f-args list
  • методы с ParamArray аргументами, которые опять же от obj, что также не кошерно

Лично мне больше всего нравится вариант с apply

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

И что? В CL нет автоматического каррирования с частичным применением.

korvin_ ★★★★★
()

Подойдёт или нет - не знаю, но можно через ParamArray + Reflection:

open System

exception MyExcp of string

type X() =
    static member apply (foo, [<ParamArray>] args: Object[]) =
        let methods_info = List.ofArray <| foo.GetType().GetMethods()
        let invoke_method = methods_info |> List.tryFind (fun m -> m.Name = "Invoke") 

        match invoke_method with
            | Some m ->
                m.Invoke(foo, args) 
            | None -> raise(MyExcp("Первый аргумент не метод!")) 


let foo x = x*x
let foo2 x y = x*y
let foo3 x y z = (x*y).ToString() + " " + z 

[<EntryPoint>]
let main _ =
    printfn "%A" <| X.apply(foo, 2)
    printfn "%A" <| X.apply(foo2, 3, 4)
    printfn "%A" <| X.apply(foo3, 2, 3, "hello")
    //printfn "%A" <| X.apply(2, 2, 3, "hello") // Exception

    Console.ReadLine() |> ignore
    0

X.apply будет возвращать Object, который потом можно скастовать к нужному типу. Из m можно вытащить ReturnType, но кастовать через :?> компилятор не даёт.

П.С. ParamArray заработал только в статик методе класса. Видимо ещё не допилили интеграцию толком.

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

П.С. у m можно вытащить список параметров и проверить их с GetType() args.

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

Кастовать надо было через downcast, тогда всё ок. Надо было:

downcast m.Invoke(foo, args)

И тогда след. код корректно компилится и выполняется:

printfn "%A" <| 2 + X.apply(foo, 2)
Norgat ★★★★★
()
Ответ на: комментарий от pseudo-cat

Если есть предложения по-другому решить, милости просим, а пока что у меня сложились три пути её решения

Откажись от eDSL, сделай DLS. С каким душе угодно синтаксисом.

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