Оборачивать sprintf, чтобы не выдавать ошибки во время компиляции, но во время выполнения? - PullRequest
1 голос
/ 22 февраля 2020

Допустим, я хотел бы иметь псевдоним sprintf, я бы просто сделал это:

namespace FSharp

module Core =
    let specialsprintf x y =
        sprintf x y

Это принесло бы мне те же преимущества во время компиляции (по сравнению с C# API двоюродного брата System.String.Format) функции sprintf, такой как проверка типов, проверка количества переданных параметров, и т. Д. c.

Однако, скажем, я хотел отключить эти тонкости времени компиляции и написать простую версию sprintf, вызвав String.Format внизу. Это могло быть возможно? Я знаю, что цель звучит глупо, но я хочу выполнить это умственное упражнение, чтобы убедиться, что я понимаю, как здесь работает F #. Если я сделаю это (предположим, что мы можем передать только один параметр):

namespace FSharp

module Core =
    let specialsprintf x y =
#if NORMAL_FSHARP
        sprintf x y
#else
        let x = x.Replace("%s", "{0}")
        System.String.Format(x,y)
#endif

Он даже не компилируется, ошибка:

~/FSharpPlayground.fs(17,17): Error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved. (FS0072) (FSharpPlayground)

Мммм, почему?

Хорошо, если я укажу тип следующим образом:

namespace FSharp

module Core =
    let specialsprintf
#if NORMAL_FSHARP
        x
#else
        (x: string)
#endif
        y =
#if NORMAL_FSHARP
            sprintf x y
#else
            let x = x.Replace("%s", "{0}")
            System.String.Format(x,y)
#endif

Тогда я получу ошибку компиляции в вызывающей программе:

~/FSharpPlaygroundUse.fs(48,48): Error FS0001: This expression was expected to have type 'obj []' but here has type 'string' (FS0001)

Полагаю, мне нужно теперь квалифицировать типа y сейчас, но я не уверен, как это сделать, на случай, если я захочу расширить его, чтобы иметь возможность использовать 2 аргумента вместо 1 (мне не удается заставить его работать с атрибутом ParamArray). Что-то подсказывает мне, что мне, вероятно, также нужна функция uncurry, но я немного растерялся: - /

1 Ответ

1 голос
/ 22 февраля 2020

Я предполагаю, что вам нужно что-то вроде sprintf, но variadi c, без проверки формата.

Для F #, к сожалению, нет функции «без сбоев» или переменных переменных. При этом есть возможность использовать массив параметров. ParamArray действительно только для членов класса, но не для привязок, поэтому мы можем согласиться на static member, который по своему объему аналогичен let fn () =.

type SpecialPrint =
    static member sprintf (format, [<ParamArray>] args) =
        let index = ref -1        
        let stringFormat = Regex.Replace(format, "%[a-z]", (fun _ -> sprintf "{%d}" (Interlocked.Increment index)))
        String.Format(stringFormat, args)

С;

let result = SpecialPrint.sprintf ("Hello %s%s", "World", "!") //Hello World!
...