каррирование нескольких функций параллельно в F # - PullRequest
0 голосов
/ 26 января 2019

В данный момент я пытаюсь выучить F # и столкнулся с проблемой, которую не могу решить и не могу найти ответы на нее в Google.

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

let LogToConsole level (format:Printf.TextWriterFormat<'T>) =
    let extendedFormat = (Printf.TextWriterFormat<string->string->'T> ("%s %s: " + format.Value))
    let date = DateTime.UtcNow.ToString "yyyy-MM-dd HH:mm:ss.fff"
    let lvl = string level
    printfn extendedFormat date lvl

с функцией printfn в качестве последней строки этой функции позволяет использовать магию, аналогичную varargs синтаксиса printf, посредством которой возвращается частично примененный метод printfn, позволяющий вызывающей стороне завершить применение аргументов.

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

Essential Я ищу, как я мог бы реализовать функцию MultiLog это позволило бы мне вызывать несколько printf-подобных функций из одного вызова функции, например, в функции ResultIWant ниже:

type LogFunction<'T> = LogLevel -> Printf.TextWriterFormat<'T> -> 'T

let MultiLog<'T> (loggers:LogFunction<'T>[]) level (format:Printf.TextWriterFormat<'T>) :'T = 
    loggers
    |> Seq.map (fun f -> f level format)
    |> ?????????

let TheResultIWant =
    let MyLog = MultiLog [LogToConsole; LogToFile]
    MyLog INFO "Text written to %i outputs" 2


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

type ThreeArg = string -> int -> bool -> unit
let funcs: ThreeArg seq = [func1; func2; func3]

let MagicFunction = ?????

// I'd like this to be valid
let partiallyApplied = MagicFunction funcs "string"

// I'd also like this to be valid
let partiallyApplied = MagicFunction funcs "string" 255

// and this (fullyApplied will be `unit`)
let fullyApplied = MagicFunction funcs "string" 255 true

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Почти нечего добавить к идеальному ответу @TomasPetricek, так как он в основном "полубог" в F #. Еще одна альтернатива, которая приходит на ум, - это использовать вычислительное выражение (см., Например: https://fsharpforfunandprofit.com/series/computation-expressions.html).. При правильном использовании оно выглядит как волшебство :). Однако у меня есть ощущение, что оно слишком тяжелое для проблемы, которую вы описали.

0 голосов
/ 26 января 2019

Чтобы ответить на конкретную часть вопроса относительно форматирования строки, есть полезная функция Printf.kprintf, которая позволяет вам делать то, что вам нужно, очень простым способом - первый параметр функции - это продолжение, которое вызывается сотформатированная строка в качестве аргумента.В этом продолжении вы можете просто взять отформатированную строку и записать ее во все нужные вам регистраторы.Вот базовый пример:

let Loggers = [printfn "%s"]

let LogEverywhere level format =
    Printf.kprintf (fun s ->
      let date = DateTime.UtcNow.ToString "yyyy-MM-dd HH:mm:ss.fff"
      let lvl = string level
      for logger in Loggers do logger (sprintf "%s %s %s" date lvl s)) format

LogEverywhere "BAD" "hi %d" 42

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

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