Type annotation for using a F# TypeProvider type e

2019-06-19 01:21发布

问题:

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
  ]

回答1:

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



回答2:

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.