I'm trying to select the text of an input field using Fable.React.
In js it can be done like this, but trying the same in Fable doesn't compile:
input [
Type "text"
OnFocus(fun e -> e.target.select())
]
How can I select the text in an input field when it is focused using Fable.React ?
The solution is to cast event.target first:
OnFocus(fun e ->
let target = e.target :?> Browser.HTMLInputElement
target.select()
)
Related
I want to define the following record type:
type LogFuncs = {
LogWithLabel : string -> 'a -> unit
LogWithLabelAndReturn : string -> 'a -> 'a }
The intention is that I can define one function of string -> unit and then use that to derive several convenience functions, like so:
let makeLogFuncs outputFunc =
let logWithLabelFunc label value = sprintf "%s: %A" label value |> outputFunc
let logWithLabelAndReturnFunc label value = logWithLabelFunc label value; value
{ LogWithLabel = logWithLabelFunc
LogWithLabelAndReturn = logWithLabelAndReturnFunc }
But, the compiler won't let me do this without specifying an <'a> when making an instance of type LogFuncs, and that's not what I want to do -- I want to be able to call this function on any 'a. (I will also want to provide related functions, hence the use of the record type.)
Is it possible to define a record type with a field containing type parameter that is not also a type parameter of the record type itself?
I don't believe it is possible to do with record types. But I can define a class which provides the mechanism I wanted:
type Logger (outputFunc: string->unit) =
member __.LogWithLabel label value = sprintf "%s: %A" label value |> outputFunc
member x.LogWithLabelAndReturn label value = x.LogWithLabel label value; value
Then I can do:
let log = new Loggery (printfn "%s")
let ``result should equal 5`` = 5 |> log.LogWithLabelAndReturn "Beans"
...and it correctly prints "Beans: 5" and returns 5.
You can make the record itself generic:
type LogFuncs<'a> = {
LogWithLabel : string -> 'a -> unit
LogWithLabelAndReturn : string -> 'a -> 'a }
That also makes the makeLogFuncs generic. It's still usable, but probably not in the way you want:
(makeLogFuncs (printfn "%s")).LogWithLabel "Number" 42
(makeLogFuncs (printfn "%s")).LogWithLabelAndReturn "Number" 42
(makeLogFuncs (printfn "%s")).LogWithLabel "Text" "Foo"
(makeLogFuncs (printfn "%s")).LogWithLabelAndReturn "Text" "Foo"
As the answer provided by Overlord Zurg implies, the OP approach is quite object-oriented, so use objects if you want to design the system in that way.
When a value is created in the F# Interactive console the inferred type and contents of the value are displayed.
How can I, at a later date, redisplay the inferred type without showing all the contents?
For example, I have an array, mydata of 1000 items. Typing mydata into the F# Interactive console will display the type, but also the contents of the array.
How about using printfn with the type like so:
F# Interactive for F# 3.1 (Open Source Edition)
Freely distributed under the Apache 2.0 Open Source License
For help type #help;;
>
val mya : int [] = [|3; 2; 5; 6; 7; 8|]
> printfn "%A" (mya.GetType());;
System.Int32[]
val it : unit = ()
You can shorten the typing required by using a little utility function:
let pm v = printfn "%A" (v.GetType())
The you can use as follows:
> pm mya;;
System.Int32[]
val it : unit = ()
"pm" stands for "print me". Call it whatever you want :)
Another approach if you don't like the type names from GetType() is just to cause an error with the value you want to evaluate. That will give you a more friendly F# type name (if you don't mind ignoring the error of course). For instance on a list you could do:
>
val myl : string list = ["one"; "two"]
> printfn myl;;
Script.fsx(195,9): error FS0001: The type 'string list' is not compatible with the type 'Printf.TextWriterFormat<'a>'
Note the type string list between the ''
Lastly you can use: (MSDN)
fsi.ShowDeclarationValues <- false
But this only silences the initial evaluation.
Unquote has an extension property for types:
> let mydata = [|Some([42])|];;
val mydata : int list option [] = [|Some [42]|]
> mydata.GetType().FSharpName;;
val it : string = "option<list<int>>[]"
I'm using the FSharp.Data.JsonProvider to read Twitter Tweets.
Playing with this sample code
https://github.com/tpetricek/Documents/tree/master/Samples/Twitter.API
I want to expand the urls in the tweet with
let expandUrl (txt:string) (url:Search.DomainTypes<...>.DomainTypes.Url) =
txt.Replace( url.Url, url.ExpandedUrl )
This results in Error:
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.
My problem is how to define the TypeProvider Type for url in the expandUrl function above?
The type inferance shows me this
val urls : FSharp.Data.JsonProvider<...>.DomainTypes.Url []
but this is not accepted in the type declaration. I assume "<...>" is not F# synatx.
How to do a type annotation for using a TypeProvider type e.g. FSharp.Data.JsonProvider<...>.DomainTypes.Url ?
Here is the complete code snippet:
open TwitterAPI // github.com/tpetricek/Documents/tree/master/Samples/Twitter.API
let twitter = TwitterAPI.TwitterContext( _consumerKey, _consumerSecret, _accessToken, _accessTokenSecret )
let query = "water"
let ts = Twitter.Search.Tweets(twitter, Utils.urlEncode query, count=100)
let ret =
[ for x in ts.Statuses do
// val urls : FSharp.Data.JsonProvider<...>.DomainTypes.Url []
let urls = x.Entities.Urls
// fully declarated to help the type inference at expandUrl
let replace (txt:string) (oldValue:string) (newValue:string) =
txt.Replace( oldValue, newValue)
// Error:
// 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.
let expandUrl (txt:string) (url:FSharp.Data.JsonProvider<_>.DomainTypes.Url) =
replace txt url.Url url.ExpandedUrl
let textWithExpandedUrls = Array.fold expandUrl x.Text urls
yield textWithExpandedUrls
]
When you call Twitter.Search.Tweets (https://github.com/tpetricek/Documents/blob/master/Samples/Twitter.API/Twitter.fs#L284), the return type of that is one of the domain types of TwitterTypes.SearchTweets, which is a type alias for JsonProvider<"references\\search_tweets.json"> (https://github.com/tpetricek/Documents/blob/master/Samples/Twitter.API/Twitter.fs#L183).
Although in the tooltip it shows up as JsonProvider<...>.DomainTypes.Url, you'll have to use the type alias TwitterTypes.SearchTweets.DomainTypes.Url
I had a similar problem trying to figure out how to use the FSharp.Data HtmlProvider.
I am using Wikipedia to get information about USA presidents. The HtmlProvider does a great job of discovering the various tables in that webpage, but I wanted to extract the logic for processing a row of "president data" into a separate function called processRow.
And the problem was trying to work out what the type of such a row is for processRow's parameter row. The following code does the trick:
#load "Scripts\load-references.fsx"
open FSharp.Data
let presidents = new HtmlProvider<"https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States">()
let ps = presidents.Tables.``List of presidents``
ps.Headers |> Option.map (fun hs -> for h in hs do printf "%s " h)
printfn ""
type Presidents = ``HtmlProvider,Sample="https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States"``.ListOfPresidents
let processRow (row:Presidents.Row) =
printfn "%d %s" row.``№`` row.President2
ps.Rows |> Seq.iter processRow
I did not type in the long type alias for Presidents, I used Visual Studio auto-completion by guessing that the type for List of presidents would be discoverable from something starting with Html, and it was, complete with the four single back quotes.
I am writing a function to return a list minus the third value.
Here is my current code:
let listString = [ "1"; "2"; "3"; "4" ];;
let del3 (listA :'a) = [listA.Head; listA.Tail.Head] # [listA.Tail.Tail.Tail];;
del3 listString
and I am getting the error:
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.
What should I change to fix the error?
I think a simpler approach based on a pattern match might be better
let del3 = function |a::b::c::d -> a::b::d | _ -> failwith "insufficient input"
You need to let the compiler know that listA is a list. Also Tail returns a list, so for the second list you're appending you don't want to wrap the tail in a list, otherwise you're going to have a list of a list:
let listString = [ "1"; "2"; "3"; "4" ]
let del3 (listA :'a list) = [listA.Head; listA.Tail.Head] # listA.Tail.Tail.Tail
del3 listString;;
A solution to handle lists of all sizes:
let del3 = function
| a::b::c::tail -> a::b::tail
| list -> list
When accessing members, methods or properties of an object, F# needs to know the type of that object. It can't just infer the type from the fact that you're accessing a property named Head because there might be many different classes that have such a property.
To fix this problem, either give listA a type annotation or use List.head and List.tail instead of the properties.
I wrote the following function to view data in a grid from F# interactive:
open System.Windows.Forms
let grid x =
let form = new Form(Visible = true)
let data = new DataGridView(Dock = DockStyle.Fill)
form.Controls.Add(data)
data.DataSource <- x |> Seq.toArray
How can I make it work for both 1D and 2D seqs? say, grid [1,2,3] or grid[(1,0);(2,0);(3,0)];; works fine but grid [1;2;3];; would not work.
another question is, why do I have to add the `|>Seq.toArray to make it work?
DataGridView uses databinding that reflects over object properties and displays them in grid columns (possibly automatically inferred). [1,2,3] and [(1,0);(2,0);(3,0)] are lists of tuples so DataGridView can show tuple components, As opposite, [1;2;3] - list of integers, it doesn't contains any properties that exposes the actual value.
Seq.ToArray is necessary because DataSource expects IList, IListSource, IBindingList or IBindingListView (DataGridView.DataSource Property ). Array implements IList, F# list - doesn't.
As desco explains, the DataGridView control displays values of properties of the object.
This is pretty silly behavior for primitive types - for example if you specify [ "Hello"; "world!" ] as the data source, it will display column Length with values 5 and 6. That's definitely not what you'd want!
The best solution I could find is to explicitly check for strings and primitive types and wrap them in a simple type with just a single property (that will get displayed):
type Wrapper(s:obj) =
member x.Value = s.ToString()
let grid<'T> (x:seq<'T>) =
let form = new Form(Visible = true)
let data = new DataGridView(Dock = DockStyle.Fill)
form.Controls.Add(data)
data.AutoGenerateColumns <- true
if typeof<'T>.IsPrimitive || typeof<'T> = typeof<string> then
data.DataSource <- [| for v in x -> Wrapper(box v) |]
else
data.DataSource <- x |> Seq.toArray
grid [ 1 .. 10 ]
grid [ "Hello"; "World" ]