Specify path to fsi.exe when creating FsiEvaluationSession - f#

As documented here, the first argument to to the argument in the array allArgs specifying the path to fsi.exe is ignored:
let argv = [| "C:\\fsi.exe" |]
let allArgs = Array.append argv [|"--noninteractive"|]
let fsiConfig = FsiEvaluationSession.GetDefaultConfiguration()
let fsiSession = FsiEvaluationSession.Create(fsiConfig, allArgs, inStream, outStream, errStream)
Is there anyway to specify this argument - i.e. specify the location of fsi.exe to be used during the evaluation?
The reason I want to do this is so I can target a specific version of fsi.exe included in the FSharp.Compiler.Tools nuget package.

Related

F# value restriction problem in reversing list

The following F# code doesn't compile due to value restriction problem:
let x = List.rev []
But this compiles:
let x = List.rev [] in 3::x
As I understand it, the compiler infers that x must be of type int list so it doesn't compile.
The following doesn't compile:
let x = List.rev [] in (3::x, true::x)
But this does:
let x = ([]) in (3::x, true::x)
Why?
As I understand it*, there are two ways to declare a value in F#:
Value has a concrete type (e.g. List<int>). You must either explicitly specify this concrete type, or the compiler must be able to infer it.
Value has a generic type (e.g. List<'t>). You must either explicitly specify this generic type, or the compiler must be able to infer it and the value must be a simple immutable value.
With that in mind, here are the explanations for what you're seeing:
// not allowed because x is generic, but not simple
let x = List.rev []
// allowed because the compiler can infer x's concrete type (List<int>)
let x = List.rev [] in 3::x
// not allowed because x is generic, but not simple
let x = List.rev [] in (3::x, true::x)
// allowed because x is both generic and simple (compiler can tell it's the empty list)
let x = ([]) in (3::x, true::x)
* The value restriction can be tricky, so there may be nuances I've overlooked in my explanation.

F# Adding value to map result in KeyNotFoundException

type bytesLookup = Map<byte,int list>
type lookupList = bytesLookup list
let maps:bytesLookup = Map.empty
let printArg arg = printfn(Printf.TextWriterFormat<unit>(arg))
let array1 = [|byte(0x02);byte(0xB1);byte(0xA3);byte(0x02);byte(0x18);byte(0x2F)|]
let InitializeNew(maps:bytesLookup,element,index) =
maps.Add(element,List.empty<int>)(*KeyNotFoundException*)
maps.[element]
let MapArray (arr:byte[],maps:bytesLookup ) =
for i in 0..arr.Length do
match maps.TryFind(arr.[i]) with
| Some(e) -> i::e
| None -> InitializeNew(maps,arr.[i],i)
MapArray(array1,maps);
printArg( maps.Count.ToString())
Exception
System.Collections.Generic.KeyNotFoundException: The given key was not
present in the dictionary. at
Microsoft.FSharp.Collections.MapTreeModule.find[TValue,a](IComparer1
comparer, TValue k, MapTree2 m) at
Microsoft.FSharp.Collections.FSharpMap2.get_Item(TKey key) at
FSI_0012.MapArray(Byte[] arr, FSharpMap2 maps) in Script1.fsx:line 16
at .$FSI_0012.main#() in Script1.fsx:line 20
In the function I'm trying to initialize a new element in the map with a list of int. I also try to push a new int value into the list at the same time.
What am I doing wrong?
F# Map is an immutable data structure, the Add method doesn't modify the existing data structure, it returns a new Map with the additions you've requested.
Observe:
let ex1 =
let maps = Map.empty<byte, int list>
maps.Add(1uy, [1]) // compiler warning here!
maps.[1uy]
Two things about this code:
It throws System.Collections.Generic.KeyNotFoundException when you run it
It gives you a compiler warning that the line maps.Add... should have type unit but actually has type Map<byte,int list>. Don't ignore the warning!
Now try this:
let ex2 =
let maps = Map.empty<byte, int list>
let maps2 = maps.Add(1uy, [1])
maps2.[1uy]
No warning. No exception. Code works as expected, returning the value [1].

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"

Type annotation required

I have the following:
let GetDateTime() = System.DateTime.UtcNow
let InternalHandle dependencies =
let getDateTime = dependencies
let future = getDateTime()
let future = getDateTime().AddDays(float 5)
printf "The time is %A" future
()
let Handle() =
let dependencies = (GetDateTime)
InternalHandle dependencies
Handle();;
but I am compiler error with "getDateTime().AddDays(float 5)" regarding type annotation.
What do I need to do to get this to work?
getDateTime is a function and I cant add a type annotation.
I am baffled why it does not pick up its a function that returns a DateTime and therefore all functions like AddDays would be available
When doing type inference on functions, the F# compiler does not look at how the function is called later on. This means that when inferring type for InternalHandle, it only sees this:
let InternalHandle dependencies =
let getDateTime = dependencies
let future = getDateTime() // (#1)
let future = getDateTime().AddDays(float 5) // (#2)
printf "The time is %A" future
From this, it can infer that dependencies is a function (because it is called in #1), but it does not know what the function returns. When it gets to (#2), it sees that you want to invoke AddDays method on the result - but this is not enough to decide what the result type is (it could be any .NET object that has this method...). For this reason, you need a type annotation to specify what object are you expecting. You can write:
let InternalHandle (dependencies : unit -> System.DateTime) =
let getDateTime = dependencies
let future = getDateTime()
let future = getDateTime().AddDays(float 5)
printf "The time is %A" future

Cast array to object

I have an array and I want to cast it to an object. This is my code.
let a = [| 1 |]
let b = a :?> obj
but it tells me that int [] has no proper subtypes and cannot be used as a source of runtime type coercion. I'm pretty sure I can always do (object) in C# without the compiler complaining, so what's the issue here?
You're trying to downcast (:?>), instead of upcast (:>). Your code should be:
let a = [| 1 |]
let b = a :> obj
Please see http://msdn.microsoft.com/en-us/library/dd233220.aspx for more details.
While ebb's answer is of course correct, there is another solution for the special case of upcasting to obj, which is the box operator:
let a = [| 1 |]
let b = box a
In F#, box doesn't have the same meaning as it does in the CLR, where it means "make a reference-type object corresponding to a value-type value." In F#, it just means "cast to obj." Because of this, you can use box with reference types as well as with value types.

Resources