Windows UI (UWP or 8.1) in F# interactive - f#

By referencing the default WPF DLLs, it's pretty easy to do anything you could do using code-only WPF:
#r "PresentationCore.dll"
#r "PresentationFramework.dll"
// ...other DLLs...
#r "WindowsBase.dll"
let window = System.Windows.Window()
let panel = System.Windows.Controls.StackPanel()
let button = System.Windows.Controls.Button()
panel.Children.Add button
button.Content <- "hi"
window.Content <- panel
window.Show()
... and you can manipulate it while the window is still open...
button.Click.Add (fun _ ->
button.Content <-
button.Content :?> string |> fun x -> (x + "!") :> obj)
...and then click the button to see it work. It seems like a pretty powerful way to build up UI components.
Is there any way to do the same thing with the Windows.UI namespace/controls/UI framework -- load some assemblies in F# interactive and instantiate UI components on the fly?
I've naively tried referencing the files that seemed relevant:
#r #"C:\Program Files (x86)\Windows Kits\10\References\Windows.Foundation.UniversalApiContract\2.0.0.0\Windows.Foundation.UniversalApiContract.winmd"
#r #"C:\Program Files (x86)\Windows Kits\10\References\Windows.Foundation.FoundationContract\2.0.0.0\Windows.Foundation.FoundationContract.winmd"
...and doing that gets me intellisense into the Windows.UI namespaces. But when I try to instantiate something:
Windows.UI.Xaml.Application()
I get:
error FS0193: Could not load file or assembly 'file:///C:\Program Files (x86)\Windows Kits\10\References\Windows.Foundation.UniversalApiContract\2.0.0.0\Windows.Foundation.UniversalApiContract.winmd' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)

There is no compiler support for WinRT assemblies, so you're not going to be able to reference an assembly as you're attempting to do and use the types in them cleanly.
On the other hand... since the .NET runtime has native support for WinRT types, you can use reflection to load those types and access their members. With a lot of effort, you could even build a type provider to provide a clean façade over that reflection and make it appear as though you can use the types directly. Here's a small example of how to directly call a WinRT API from F# via reflection:
open System.Reflection
let (?) (o:obj) s : 'a =
let rec build ty args =
if Reflection.FSharpType.IsFunction ty then
let dom, rng = Reflection.FSharpType.GetFunctionElements ty
let mkArgs =
if dom = typeof<unit> then
if Reflection.FSharpType.IsFunction rng then failwith "Unit as non-final argument in curried definition?"
fun _ -> args
else
fun arg -> arg::args
Reflection.FSharpValue.MakeFunction(ty, fun o -> build rng (mkArgs o))
else
let rcvr,ty,flags =
match o with
| :? System.Type as ty -> null,ty,BindingFlags.Static
| _ -> o,o.GetType(),BindingFlags.Instance
let flags = flags ||| BindingFlags.Public
let meth =
if Reflection.FSharpType.IsFunction typeof<'a> then
query {
for m in ty.GetMethods(flags) do
where (m.Name = s)
where (m.GetParameters().Length = args.Length)
exactlyOne
}
else
ty.GetProperty(s, flags).GetGetMethod()
meth.Invoke(rcvr, args |> List.toArray)
build typeof<'a> [] :?> 'a
let Clipboard = System.Type.GetType(#"Windows.ApplicationModel.DataTransfer.Clipboard, Windows.ApplicationModel, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime")
Clipboard?GetContent()?AvailableFormats |> Seq.iter (printfn "%s")

My understanding is that there is no F# support for UWP yet.
See for instance this fresh open issue.

Related

How to use results of FSharp.Compiler.Services

I'm trying to build a system that is similar to FsBolero (TryWebassembly), Fable Repl and many more that uses Fsharp.Compiler.Services.
So I expect it is feasible to achieve my goals but I encountered a problem that I hope is only a result of my lack of experience with that realm of software development
I'm implementing a service that gives user the power to write custom algorithms (DSL) in the context of the domain system.
The code to compile come as a plain raw string that is fully correct F# code.
Sample DSL algorithm looks like:
let code = """
module M
open Lifespace
open Lifespace.LocationPricing
let alg (pricing:LocationPricing) =
let x=pricing.LocationComparisions.CityLevel.Transportation
(8.*x.PublicTransportationStation.Data+ x.RailwayStation.Data+ 5.*x.MunicipalBikeStation.Data) / 14.
"""
that code compiles correctly via CompileToDynamicAssembly. I also provided proper reference to my domain *.dll via -r Fsc parameter.
And here comes my problems as next I have the generated dynamic assembly and want to invoke that algorithm.
I do it with reflection (is there any other way?) with
f.Invoke(null, [|arg|]) when arg is of type LocationPricing and comes from main/hosting project reference.
The Invoke doesn't work because I have error:
Cannot cast LocationPricing to LocationPricing
I had the same problem when tried to use F# interactive services, the error was similar:
Cannot cast [A]LocationPricing to [B]LocationPricing
I'm aware I have two same dlls in the context and F# does have extern alias syntax to solve it.
But other mentioned public systems somehow deals with that or I'm doing it wrongly.
I will look at code of Bolero and FableRepl but it will definately take some time to understand the pitfalls.
Update: Full code (Azure Function)
namespace AzureFunctionFSharp
open System.IO
open System.Text
open Microsoft.Azure.WebJobs
open Microsoft.Azure.WebJobs.Extensions.Http
open Microsoft.AspNetCore.Http
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.Logging
open FSharp.Compiler.SourceCodeServices
open Lifespace.LocationPricing
module UserCodeEval =
type CalculationResult = {
Value:float
}
type Error = {
Message:string
}
[<FunctionName("UserCodeEvalSampleLocation")>]
let Run([<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)>] req: HttpRequest, log: ILogger , [<Blob("ranks/short-ranks.json", FileAccess.Read)>] myBlob:Stream)=
log.LogInformation("F# HTTP trigger function processed a request.")
// confirm valid domain dll location
// for a in System.AppDomain.CurrentDomain.GetAssemblies() do
// if a.FullName.Contains("wrometr.lam.to.ranks") then log.LogInformation(a.Location)
// let code = req.Query.["code"].ToString()
// replaced just to show how the user algorithm can looks like
let code =
"""
module M
open Lifespace
open Lifespace.LocationPricing
open Math.MyStatistics
open MathNet.Numerics.Statistics
let alg (pricing:LocationPricing) =
let x= pricing.LocationComparisions.CityLevel.Transportation
(8.*x.PublicTransportationStation.Data+ x.RailwayStation.Data+ 5.*x.MunicipalBikeStation.Data) / 14.
"""
use reader = new StreamReader(myBlob, Encoding.UTF8)
let content = reader.ReadToEnd()
let encode x = LocationPricingStore.DecodeArrayUnpack x
let pricings = encode content
let checker = FSharpChecker.Create()
let fn = Path.GetTempFileName()
let fn2 = Path.ChangeExtension(fn, ".fsx")
let fn3 = Path.ChangeExtension(fn, ".dll")
File.WriteAllText(fn2, code)
let errors, exitCode, dynAssembly =
checker.CompileToDynamicAssembly(
[|
"-o"; fn3;
"-a"; fn2
"-r";#"C:\Users\longer\azure.functions.compiler\bin\Debug\netstandard2.0\bin\MathNet.Numerics.dll"
"-r";#"C:\Users\longer\azure.functions.compiler\bin\Debug\netstandard2.0\bin\Thoth.Json.Net.dll"
// below is crucial and obtained with AppDomain resolution on top, comes as a project reference
"-r";#"C:\Users\longer\azure.functions.compiler\bin\Debug\netstandard2.0\bin\wrometr.lam.to.ranks.dll"
|], execute=None)
|> Async.RunSynchronously
let assembly = dynAssembly.Value
// get one item to test the user algorithm works in the funtion context
let arg = pricings.[0].Data.[0]
let result =
match assembly.GetTypes() |> Array.tryFind (fun t -> t.Name = "M") with
| Some moduleType ->
moduleType.GetMethods()
|> Array.tryFind (fun f -> f.Name = "alg")
|>
function
| Some f -> f.Invoke(null, [|arg|]) |> unbox<float>
| None -> failwith "Function `f` not found"
| None -> failwith "Module `M` not found"
// end of azure function, not important in the problem context
let res = req.HttpContext.Response
match String.length code with
| 0 ->
res.StatusCode <- 400
ObjectResult({ Message = "No Good, Please provide valid encoded user code"})
| _ ->
res.StatusCode <-200
ObjectResult({ Value = result})
**Update: changing data flow **
To move forward I resigned to use domain types in both places. Instead I do all logic in domain assembly and only pass primitives (strings) to reflected invocation. I'm also suprised a lot that caching still works everytime I do compilation on each Azure Function call. I will experiment as well with FSI, in theory it should be faster than reflection but with additional burden to pass parameters to evaluations
In your example, the code that runs inside your dynamically compiled assembly and the code calling it need to share a type LocationPricing. The error you are seeing typically means that you somehow ended up with different assembly loaded in the process that is calling the dynamically compiled code and the code actually running the computation.
It is hard to say exactly why this happened, but you should be able to check whether this is indeed the case by looking at assemblies loaded in the current App Domain. Say that your shared assembly is MyAssembly. You can run:
for a in System.AppDomain.CurrentDomain.GetAssemblies() do
if a.FullName.Contains("MyAssembly") then printfn "%s" a.Location
If you were using F# Interactive Services, then a trick to fix this is to start an FSI session and then send an interaction to the service that loads the assembly from the right place. Something along those lines:
let myAsm = System.AppDomain.CurrentDomain.GetAssemblies() |> Seq.find (fun asm ->
asm.FullName.Contains("MyAssembly"))
fsi.EvalInteraction(sprintf "#r #\"%s\"" myAsm.Location)

Does the >>= operator not take a function?

I'm working on a side project and I'm using Hopac for the first time. I ran into an odd (to me) compilation issue that I haven't been able to grok. I suspect that I'm the problem here, and not Hopac.
The program is supposed to be a simple console app that consumes notifications from various services. Here's the problematic module:
module Provider
open System
open System.IO
open Hopac
open BitThicket.NotificationHelper.Core
open BitThicket.NotificationHelper.Providers
let defaultProviderTypes =
[| typeof<GitHub.GitHubNotificationProvider> |]
type Provider = {
getCh : Ch<Providers.INotification seq>
}
let giveLatest ch latest =
Ch.give
let start config logger (providerType:Type) = Job.delay <| fun () ->
let providerImpl = Activator.CreateInstance(providerType) :?> Providers.INotificationProvider
let p = { getCh = Ch() }
let rec server =
let latest = providerImpl.GetLatestNotificationsAsync(None) |> Job.fromAsync
latest >>= Ch.give p.getCh // error here
}
Job.start server
In this case, the compiler complains: Expecting a type supporting the operator '>>=' but given a function type. You may be missing an argument to a function.
Similarly, if I use a slightly different syntax:
// ...
let rec server =
let latest = providerImpl.GetLatestNotificationsAsync(None) |> Job.fromAsync
latest >>= fun l -> Ch.give p.getCh l // error here
// ...
In this case, the error is: This function takes too many arguments, or is used in a context where a function is not expected.
I asked haf about his in slack, and his suggestion was to check for alternative definitions of >>=. The tooling doesn't really do much to help me figure that one out, but the only namespace/module I have opened that defines >>= is Hopac (the BitThicket ones are just trivially simple namespaces with some type definitions in them).
What am I doing wrong here?
I'm looking at the source code, and I see that the bind operator is actually defined in Hopac.Infixes, not in Hopac.

F# Type Provider dependency resolution - "Could not load file or assembly..."

I am encountering a "Could not load file or assembly... ... The system could not find the file specified" error when trying to consume my type provider.
The error appears on building the consuming application but does not show up as a 'red squiggly' in visual studio prior to the build.
I have copied my TP below but the issue occurs inside the Database.listDbs call and I strongly suspect the issue is not the code below but how I am packaging the dependencies.
I call into the Microsoft.Azure.DocumentDB package which, in turn, depends on Newtonsoft.Json. It is the Newtonsoft.Json package which cannot be found. I am using Paket to manage dependencies and have redirects on.
The full code (including all the paket files) is on github here: https://github.com/stewart-r/AzureDocumentDbTypeProvider/tree/dependency-issue
I found this question which seems very similar but the solution did not make any difference.
My TP code is as follows:
namespace ProviderImplementation
open ProviderImplementation.ProvidedTypes
open Microsoft.FSharp.Core.CompilerServices
open System.Reflection
open System
open Config
open Database
[<TypeProvider>]
type public DocumentDbTypeProvider(config: TypeProviderConfig) as this =
inherit TypeProviderForNamespaces()
let thisAssembly = Assembly.GetExecutingAssembly()
let docDbType = ProvidedTypeDefinition(thisAssembly,namespaceName,"DocumentDbTypeProvider", baseType = Some typeof<obj>)
let initFn (typeName : string) (args : obj []) =
let acProvidedType = ProvidedTypeDefinition(thisAssembly, namespaceName, typeName, baseType = Some typeof<obj>)
acProvidedType.AddMember(ProvidedConstructor(parameters = [], InvokeCode = (fun args -> <## null ##>)))
let getDbProperties () =
Database.listDbs (args.[0] :?> string) (args.[1]:?> string)
|> List.map(fun d -> new ProvidedProperty(d.Name, typeof<string>, IsStatic = true, GetterCode = (fun _ -> <## "Test db name" ##>)))
acProvidedType.AddMembers(getDbProperties())
acProvidedType
let parameters =
[ ProvidedStaticParameter("accountEndPointUri", typeof<string>, String.Empty)
ProvidedStaticParameter("accountKey", typeof<string>, String.Empty)]
do
docDbType.DefineStaticParameters(parameters,initFn)
this.AddNamespace(namespaceName,[docDbType])
[<TypeProviderAssembly>]
do ()
This is a binding redirect issue - you need to handle the BR inside the type provider. Alternatively, you can restrict the dependencies to the minimum version needed by your direct dependency e.g. DocumentDB.
Have you tried making sure that your "TP dependencies are located in the same folder that the TP itself resides in"?
It sounds like you have the same issue as described in this answer:
https://stackoverflow.com/a/33889287/371698 (quote from this answer)

Build a Linq expression from an F# function, to pass to C#

I am trying use the Lex.Db database in an F# project in a WinRT 8.1 app.
I am following this tutorial for C#. I've successfuly added a reference to Lex.Db to an F# project and the simple calls in the tutorial translate to f# and compile (eg. let db = new DbInstance("demo")).
The problem is this C# code:
db.Map<Contact>().Key(i => i.Id)
-- Edit --
To save others from reading further, in F# 3.0 this is almost a non-problem. See kvb's comment below. The solution is:
db.Map<Contact>().Key(fun i => i.Id)
-- /Edit ---
The Key function expects an Expression<Func<Contact,'a>> and I am unable to build this in F#.
A very similar question appears in How to pass LinQ Expressions from F# to C# code. The recommended solution is to use LeafExpressionConverter.QuotationToLambdaExpression.
I have tried this as follows:
type Contact() =
member val Id = 0 with get, set
member val FirstName = "" with get, set
let db = new DbInstance("Demo")
let idExpr = LeafExpressionConverter.QuotationToLambdaExpression
<# fun (c : Contact) -> c.Id #>
db.Map<Contact>().Key(idExpr) |> ignore // <- Error
This produces a compiler error on idExpr:
Type mismatch. Expecting a Expression<Func<Contact,'a>> but given a Expression<(Contact -> int)>. The type 'Func<Contact,'a>' does not match the type 'Contact -> int'.
This question: Expression<Func<T, bool>> from a F# func seems to address the problem directly, but the solution uses Microsoft.FSharp.Linq.QuotationEvaluation, which I can't find in F# 3.0 for WinRT.
How do I turn <# fun (c : Contact) -> c.Id #> into an Expression<Func<Contact,'a>>?
Microsoft.FSharp.Linq.QuotationEvaluation is in PowerPack, but as of v3.0 the functionality it provides is available in Core via LeafExpressionConverter. You can use the code in the question you linked, but change it to use LeafExpressionConverter for the translation part.
open System
open System.Linq.Expressions
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers
let toLinq (expr : Expr<'a -> 'b>) =
let linq = LeafExpressionConverter.QuotationToExpression expr
let call = linq :?> MethodCallExpression
let lambda = call.Arguments.[0] :?> LambdaExpression
Expression.Lambda<Func<'a, 'b>>(lambda.Body, lambda.Parameters)

The field, constructor or member 'AsyncReadToEnd' is not defined error

I am learning RX (Reactive Extensions), and have found someone posted some code nearly one year ago using F# and RX to make a simple webCrawler. I tried to see if I can re-use the code. I download RX, and create a F# windows application, add reference to System.Reactive. My IDE is VS 2010 Ultimate, RX version is: 1.1.11111. The following is the code:
#light
open System
open System.Linq
open System.Collections.Generic
open System.Net
open System.IO
open System.Threading
open System.Text.RegularExpressions
open System.Reactive
open System.Reactive.Linq
let create f =
Observable.Create<_>(fun x ->
f x
new System.Action((fun () -> ())))
let ofAsync async =
create (fun obs -> Async.StartWithContinuations(async, obs.OnNext,obs.OnError,obs.OnError))
let fromEvent (event:IEvent<_,_>) = create (fun x -> event.Add x.OnNext)
let tickEvent = new Event<unit> ()
let tickEventObs = tickEvent.Publish |> fromEvent
let fetch(url:string) =
async { let req = WebRequest.Create(url)
let! resp = req.AsyncGetResponse()
let stream = resp.GetResponseStream()
let reader = new StreamReader(stream)
let! html = reader.AsyncReadToEnd()
return html
} |> ofAsync
But the code can not get compiled, I got the error message:
Error 1 The field, constructor or member 'AsyncReadToEnd' is not defined
So the error was on this line:
let! html = reader.AsyncReadToEnd()
I guess there could be some changes for the past one year in RX or F#.
For my current environment, what is the correct way to re-write the above code?
Thanks and happy new year to you all!
John
AsyncReadToEnd() extension method of StreamReader is part of FSharpPowerPack now. Install FSharpPowerPack from this link, if not yet, then add reference to FSharp.PowerPack to your project. This should make AsyncReadToEnd()method accessible from the rest of your code.
Use the dedicated AsyncStreamReader type
AsyncReadToEnd() extension method does not exists anymore in the FSharp.PowerPack.
It has been replaced with the AsyncStreamReader dedicated type that contains proper asynchronous implementation of stream reading (like ReadToEnd, ReadLine, etc.)
It can be used like that:
async {
use asyncReader = new AsyncStreamReader(stream)
return! asyncReader.ReadToEnd() }
Note: Once you have installed FSharp.PowerPack, the AsyncStreamReader type will be 'injected' in the Microsoft.FSharp.Control namespace
Other related answer: https://stackoverflow.com/a/7925440/1480391

Resources