How do I pass optional arguments in extension methods? - f#

This code does not work because I am getting options and optional arguments muddled up.
How can I pass an option to an optional argument?
type Foo() =
member this.Bar(?name : string, ?number : int) =
let name = defaultArg name "johndoe"
let number = defaultArg number 0
name + "-" + string number
[<AutoOpen>]
module FooExtensions =
open System
type Foo with
member this.Bar(?name : string, ?numberAsString : string) =
let number =
numberAsString
|> Option.map Int32.Parse
this.Bar(name=name, number=number) // Invalid

You can put the question mark prefix on a named parameter when calling the method, to indicate that you want to pass a value as an option.
this.Bar(?name=name, ?number=number)

Related

F# Chaining functions that return tuple

I am new to F# and am trying to chain functions to make a Higher Order Function.
A simplified example is
init returns a tuple
validate accepts a tuple and returns bool
let init : string * string =
("1", "2")
let validate ((a: string), (b: string)) : bool =
a.Equals(b)
let test = init >> validate
ERROR
This expression was expected to have type 'a -> 'b' but here has type 'string * string'
As the answer for Piotr explains, you are getting an error because you have a value and a function. To compose those, you can turn init into a function, but you do not really need to use composition in this case.
If you want to pass a value as an argument to a function, it is typically much simpler to just pass it as an argument:
let res = validate init
Alternatively, if you have a number of functions you want to apply to your input in a sequence, you can do this using the piping operator:
let res = init |> validate
Function composition using >> is a nice functional trick, but I think it is actually much less common in standard F# code than most people think. I use |> all the time, but >> only rarely.
You can only compose functions using the >> combinator. Your first assignment is not a function - it is a binding to a value - your tuple.
You can convert it to a function just by adding empty parameter list () (unit) parameter like this:
let init() : string * string =
("1", "2")
let validate ((a: string), (b: string)) : bool =
a.Equals(b)
let test = init >> validate
let res = test()

How to wrap printfn in F#?

I have read about continuations and partial applications; I am also aware of the kprintf function.
But I still don't know how to write something like:
let myPrintFunction format variable_length_arguments_list =
let a = sprintf format variable_length_ argument_list
do other things
what would be the syntax this this?
so I could use it like:
myPrintFunction "%s : %i" "hello" 3
Edit:
This is different than How do I implement a method with a variable number of arguments? because that question is asking how to make a method with a variable number of arguments, but the issue I am facing is to pass that variable number of argument to the next function (sprintf) that takes a variable number of arguments too.
Or, at least that's where I suppose the problem is.
The test code, based on the solution proposed by Scott can be found here: https://dotnetfiddle.net/oCzcS9
I want to demonstrate the ksprintf function, because that one accepts a continuation that will allow you to pass on the resulting string to e.g. a log system.
For the purpose of demonstration, let's first create something that can take a single string as input and pass it on, in this case to the console.
let writeStringToConsole (s: string) = Console.WriteLine ("OUTPUT : " + s)
So now, if writeStringToConsole is all we have, how to we make it accept F# formatting?
let printToConsole format = Printf.ksprintf writeStringToConsole format
Example that demonstrates that it works.
type DU = A | B
let i = 7
let s = "thirteen"
let du = B
printToConsole """an int %i and a string "%s" here""" i s
printToConsole """an int %i and a string "%s" and DU %A here""" i s du
// OUTPUT : an int 7 and a string "thirteen" here
// OUTPUT : an int 7 and a string "thirteen" and DU B here
// Note that OUTPUT is also part of the actual output,
// and it demonstrates how you can add e.g. a timestamp
// or line number or something to the output string, without
// it being part of the formatting.
edit: Some additional notes
The format string must be a literal. That's because the literal string must be read at compile time in order to compute the function that must be returned in order to gobble up whatever values/types that follow the format string.
For example, if you do printToConsole "%i %s %A %A" 7 "x" myType yourType, then you'll see int -> string -> MyType -> YourType in the signature of printToConsole where it's used.
There is a way to use plain strings as format strings with this system, but I don't remember how it's done, and anyway it spoils the type safety. It comes in handy when doing internationalization of strings, and your format strings must come from a resource and not F# source due to external translator services.
edit 2 : Wrap e.g. log system
I created an interface to use for various logging systems, which pretty much share the same features.
type ILogger =
...
abstract member Debugf: StringFormat<'h, unit> -> 'h
abstract member Verbosef: StringFormat<'h, unit> -> 'h
abstract member Infof: StringFormat<'h, unit> -> 'h
abstract member Warningf: StringFormat<'h, unit> -> 'h
abstract member Errorf: StringFormat<'h, unit> -> 'h
abstract member Fatalf: StringFormat<'h, unit> -> 'h
Then an implementation for my currently used logging system looks like this.
type internal SiLogger(session: Session) =
let slogf = Printf.ksprintf
...
interface ILogger with
...
member _.Debugf format = slogf session.LogDebug format
member _.Verbosef format = slogf session.LogVerbose format
member _.Infof format = slogf session.LogMessage format
member _.Warningf format = slogf session.LogWarning format
member _.Errorf format = slogf session.LogError format
member _.Fatalf format = slogf session.LogFatal format
And there is a null logger.
let slogf = Printf.ksprintf
let dummyLog _ = () // The parameter is the title string.
let dummy format = slogf dummyLog format
let getNullLogger () =
{ new ILogger with
...
member _.Debugf format = dummy format
member _.Verbosef format = dummy format
member _.Infof format = dummy format
member _.Warningf format = dummy format
member _.Errorf format = dummy format
member _.Fatalf format = dummy format
...
}
open System
let myPrintFunction (format: Printf.StringFormat<_>) ([<ParamArray>] args) =
let a = sprintf format args
a
myPrintFunction "%s : %i" "hello" 3
To add the PrintF as a member function, this is the closest I could get. As you see, I had to pass the format string separately (in the constructor, or I could have used a property setter). I could find no way to pass the format string as the first parameter of the PrintF function as I could for a free function (see my other answer at https://stackoverflow.com/a/58822618/5652483).
Also, if I uncomment the line this.RaiseSomeEvent msg, then it breaks. So I could find no way to enable the PrintF function to have a side effect.
Hopefully, someone else can solve these issues.
type Foo (format: Printf.StringFormat<_>) =
member this.RaiseSomeEvent msg = printf "%s" msg
member this.PrintF ([<ParamArray>] args) =
let msg = sprintf format args
//this.RaiseSomeEvent msg
msg
let foo = Foo("%s : %i")
foo.PrintF "hello" 3

Extend F# Arrays with lookup for bigint

I would like to extend F# Arrays such that I can use arrays without converting to the finite int. Instead I want to work with bigint directly.
I was able to add a second length method to the array type as follows:
type 'T ``[]`` with
member this.LengthI: bigint =
bigint this.Length
member this.Item(index: bigint): 'T =
this.[int index]
However the Item method cannot be called with the .[ ] syntax.
Any ideas how this could be achieved? I this possible at all?
I strongly suspect this isn't possible for native arrays. You can verify yourself that you can overload indexed access just fine for other collections.
If you compile the following code:
let myArray = [| "a" |]
let myList = [ "a" ]
let arrayElement = myArray.[11111]
let listElement = myList.[22222]
and inspect the resulting IL, you'll see that while accessing the list element compiles to a regular virtual call, there is a special CIL instruction for accessing a native array element, ldelem.
//000004: let arrayElement = myArray.[11111]
IL_002c: call string[] Fuduoqv1565::get_myArray()
IL_0031: ldc.i4 0x2b67
IL_0036: ldelem [mscorlib]System.String
IL_003b: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::arrayElement#4
.line 5,5 : 1,33 ''
//000005: let listElement = myList.[22222]
IL_0040: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string> Fuduoqv1565::get_myList()
IL_0045: ldc.i4 0x56ce
IL_004a: callvirt instance !0 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>::get_Item(int32)
IL_004f: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::listElement#5
IL_0054: ret
I would guess that the same compiler logic that special-case array access to that single instruction also bypass any overload resolution involving extension methods and the like.
One way to circumvent this is to wrap the array in a custom type, where overloaded indexers will work as you expect. Making the wrapper type a struct should reduce the performance loss in most cases:
type [<Struct>] BigArray<'T>(array : 'T[]) =
member this.LengthI: bigint =
bigint array.Length
member this.Item
with get(index : int) = array.[index]
and set (index : int) value = array.[index] <- value
member this.Item
with get(index : bigint) = array.[int index]
and set (index : bigint) value = array.[int index] <- value
let bigArray = BigArray myArray
let bigArrayElement = bigArray.[0]
let bigArrayElement2 = bigArray.[bigint 0]
Another one is to upcast the array to the base System.Array class, on which you can then define the same overloaded operator. This removes the need to create a wrapper type and duplicate all members of 'T[], as you can just upcast/downcast the same array object as necessary. However, since the base class is untyped, you will lose type safety and have to box/unbox the elements when using the indexed access, which is quite ugly:
type System.Array with
member this.Item
with get (index : int) = (this :?> 'T[]).[index]
and set (index : int) (value : 'T) = (this :?> 'T[]).[index] <- value
member this.Item
with get(index : bigint) : 'T = (this :?> 'T[]).[int index]
and set(index : bigint) (value : 'T) = (this :?> 'T[]).[int index] <- value
let untypedArray = myArray :> System.Array
let untypedArrayElement = box untypedArray.[0] :?> string
let untypedArrayElement2 = box untypedArray.[bigint 0] :?> string

Generic function to read from a `.json` file

So, I can easily write an arbitrary type to JSON with Newtonsoft.Json:
type X = {
Number: decimal
Sequence: decimal
NumList: decimal list
}
let createItem (n, s, nL) =
{Number = n;
Sequence = s;
NumList = nL}
let items =
[
(1M, 1M, [1M; 2M; 3M])
(2M, 2M, [2M; 4M; 6M])
(3M, 3M, [3M; 6M; 9M])
]
|> List.map createItem
open Newtonsoft.Json
open System.IO
let writeToJson (path: string) (obj: 'a) : unit =
let serialized = JsonConvert.SerializeObject(obj)
File.WriteAllText(path, serialized)
writeToJson "xList.json" items
How can I write a function generic enough that I can read a JSON file? In other words, I'd like something like:
let readFromJson (path: string) (t: 'T) =
let convertToQr = File.ReadAllText(path)
Newtonsoft.Json.JsonConvert.DeserializeObject<t list>(convertToQr)
where the second argument is the Type of the object in path, but I don't know how to do that. If I try to use this function as is, I get a compiler error.
How can I declare in the second argument above the type of the thing that is in path? Can I?
Generic parameters, when explicitly defined, are written in angle brackets immediately after function name, before regular parameters:
let readFromJson<'T>(path: string) =
let convertToQr = File.ReadAllText(path)
Newtonsoft.Json.JsonConvert.DeserializeObject<'T list>(convertToQr)
Usage:
readFromJson<string> "/some/file.json"
Alternatively, you can specify the return type of your function, and let the compiler infer all generic parameters and arguments for you:
let readFromJson(path: string) : 't list =
let convertToQr = File.ReadAllText(path)
Newtonsoft.Json.JsonConvert.DeserializeObject(convertToQr)
Here, the compiler knows that the generic argument of DeserializeObject must be 't list, because its result is being returned from readFromJson, and the result type of readFromJson is explicitly declared to be 't list. Similarly, just by noticing a generic type in the function definition, the compiler will infer that the function has one generic parameter.
In a similar way, you can let the compiler infer the required type when you call the function:
// call inferred to readFromJson<string>, because that's the required return type
let s: string list = readFromJson "/some/file.json"

Cannot assign to a parameter

I have declared a function
func someFunction(parameterName: Int) {
parameterName = 2 //Cannot assign to let value parameter Name
var a = parameterName
}
and trying to assign it a value during runtime, but it gives me error
"Cannot assign to let value parameter Name".
Is the parameter name constant by default? Can I change it to a variable?
[In Swift >= 3.0] Function parameters are defined as if by let and thus are constants. You'll need a local variable if you intend to modify the parameter. As such:
func someFunction (parameterName:Int) {
var localParameterName = parameterName
// Now use localParameterName
localParameterName = 2;
var a = localParameterName;
}
[In Swift < 3.0] Declare the argument with var as in:
func someFunction(var parameterName:Int) {
parameterName = 2;
var a = parameterName;
}
use of inout has a different semantics.
[Note that "variable parameters" will disappear in a future Swift version.] Here is the Swift documentation on "variable parameters":
Function parameters are constants by default. Trying to change the
value of a function parameter from within the body of that function
results in a compile-time error. This means that you can’t change the
value of a parameter by mistake.
However, sometimes it is useful for a function to have a variable copy of a parameter’s value to work with. You can avoid defining a
new variable yourself within the function by specifying one or more
parameters as variable parameters instead. Variable parameters are
available as variables rather than as constants, and give a new
modifiable copy of the parameter’s value for your function to work
with.
Define variable parameters by prefixing the parameter name with the keyword var: ..."
Excerpt From: Apple Inc. “The Swift Programming Language.”
If you actually want to change the value stored in a location that is passed into a function, then, as #conner noted, an inout parameter is justified. Here is an example of that [In Swift >= 3.0]:
1> var aValue : Int = 1
aValue: Int = 1
2> func doubleIntoRef (place: inout Int) { place = 2 * place }
3> doubleIntoRef (&aValue)
4> aValue
$R0: Int = 2
5> doubleIntoRef (&aValue)
6> aValue
$R1: Int = 4
In order to modify the argument passed in, you have to designate it as an inout parameter:
func someFunction(inout parameterName:Int)
{
parameterName = 2;
var a = parameterName;
}
Note this will change the variable that was passed in as well. If that isn't what you're looking for, use var as GoZoner suggested.

Resources