how do I do foo[bar] (partially dynamic property access) in fable? - fable-f#

I have a fairly simple javascript method
(props,propName,componentName) => {
var value = props[propName];
const getOrSpread = name =>
props[name] || props.spread && props.spread[name];
// remainder of function code omitted
}
that is working in javascript land. I'm trying to convert it to fable but either I can get it to have a definitely exists property access to .spread or dynamic access to props[propName] but not both
module JsxHelpers =
type IReactProps =
abstract member spread : obj
let isfuncOrNullPropType (props:IReactProps) (propName:string) componentName =
let propsO :obj = box props
let value:obj = propsO?propName
let valueAttempt2:obj = (box props)?(propName)
// rest of translation not attempted yet
value
where if props is defined as IReactProps, then .spread works, but neither of the two possible let value lines compile.
or props is defined as obj and it says `This expression was expected to have type 'obj' but here has type ''a -> obj'
even the simplest object from the documentation doesn't appear to compile:
let isfuncOrNullPropType (props:obj) (propName:string) =
let value2:obj = props?propName
value2
using "fable-core": "^1.0.0-narumi-905"

You definitely need to put the prop name in parentheses according to the documentation. The compiler error you're getting is because props?(propName) returns type 'a -> obj. Apparently, the dynamic (?) operator returns an Applicable, and from the fable source:
/// DO NOT USE: Internal type for Fable dynamic operations
type Applicable = obj->obj
Perhaps try:
let value : obj = unbox<obj> (props?(propName))

Related

GUI update patterns in F#

I'm looking for the best way to propagate model updates to the GUI, using a "classic" (as in: non reactive functional) GUI toolkit: Terminal.GUI. Currently I have this code (simplified):
type Tui(state: StateManager) =
let state = state
let window = Window(bla bla bla)
let lblPath = Label(bla bla bla)
let lstView =
{ new ListView(bla bla bla) with
member this.ProcessKey(k: KeyEvent) =
let updateViews() =
Application.MainLoop.Invoke(fun () ->
this.SetSource model.CurrentState.LstData
lblPath.Text <- ustr model.CurrentState.CurrPath)
match k.Key with
| Key.CursorRight ->
state.changeTheState()
updateViews()
true
| _ -> true }
do
Application.Init()
// add all GUI components: window.add(lblPath), etc
Application.Run()
// XXX repetition of updateViews() above!
Application.MainLoop.Invoke(fun () ->
lstView.SetSource model.CurrentState.LstData
lblPath.Text <- ustr model.CurrentState.CurrPath)
The issue here is that the code to update the view components is duplicated. I believe this is because:
in that ProcessKey method of the ListView object expression I cannot access any external method of the Tui class (this is probably also because F# compiler is one-pass only (?))
outside that method I cannot access the updateView function
Is there a better way of doing this that would avoid code repetition? Am I using the wrong pattern GUI update pattern?
(Complete code is here)
Of course, it doesn't need to be too complicated - refactor your update to just take in a listview parameter:
let updateViews (lstView: ListView) =
Application.MainLoop.Invoke(fun () ->
lstView.SetSource state.CurrentState.LstData
...
)
And inside the member definition, call:
updateViews(this)
And below, you can use updateViews lstView.
When you use an object expression, the type of the expression becomes the type you specify in new <type>, so any type augmentations you do inside won't make it outside. For a more OOP approach, declare an intermediate type:
[<AbstractClass>]
type UpdateableList() =
inherit ListView([||])
abstract member Update: unit -> unit
implement your update logic:
{ new UpdateableList(X = Pos.At(0), Y = Pos.At(2), ...) with
member this.Update() =
...
and in your setup, you have access to a public method:
lstView.Update()

How to typecast NSManagedObject class to Any to create a BehaviorRelay object in RxSwift?

I have an Entity called 'CartItem' (NSManagedObject class) in my coredata datamodelid. Wanted to change Variable to BehaviorRelay in RxSwift, but I get a compile time error stating "[Any] is not convertible to CartItem". How do I change Variable to BehaviorRelay?
var cartItemsArray = BehaviorRelay<CartItem>(value: []) //get error here
//var cartItemsArray: Variable<[CartItem]> = Variable([])
You forgot the brackets around CartItem.
let cartItemsArray = BehaviorRelay<[CartItem]>(value: [])
And don't make cartItemsArray a var, it should be a let.

F# Query Expressions: cannot use "for"!

For whatever reason, I cannot use a "for" construct within a query expression. What I get is the following error. The code I just what I picked that used to work -->
"error: This control construct may only be used if the computation expression builder defines a 'For' method"
#r "System.Data.Linq.dll"
#r "FSharp.Data.TypeProviders.dll"
open FSharp.Linq
open FSharp.Data.TypeProviders
[<Literal>]
let connectionString' = #"
data source = ...;
initial catalog = NORTHWND;
Integrated Security = SSPI"
type NorthwndDb =
SqlDataConnection<connectionString', Pluralize = true>
let db' = NorthwndDb.GetDataContext()
let curstomerSortedByCountry =
query { for c in db'.Customers do
sortBy c.Country
select (c.Country, c.CompanyName) }
|> Seq.cache
I was not able to test this with your code snippet (don't have a database to test this against), but I think the kind of error you are reporting would happen if you defined a variable named query somewhere in your code snippet before the query expression. For example:
let query = "oops!"
let curstomerSortedByCountry =
query { for c in [1 .. 10] do
sortBy c
select c }
error FS0708: This control construct may only be used if the computation expression builder defines a 'For' method
The reason for this is that the identifier query in query { .. } is just a variable declared in the standard F# library that has various members including query.For. If you hide this with your own declaration, then the member will not be found.

Iterating over a collection in Swift: var vs. let

I have a method that iterates over an array and call other method with every element as argument. If I declare this method as:
func didFinishedListFiles(files: [FileModel]) {
for var fileData in files {
self.downloadSingleFile(NSUUID(UUIDString: fileData.uuid!)!);
}
}
Xcode shows a warning:
Variable 'fileData' was never mutated; consider changing to 'let' constant
But if I change var to let:
func didFinishedListFiles(files: [FileModel]) {
for let fileData in files {
self.downloadSingleFile(NSUUID(UUIDString: fileData.uuid!)!);
}
}
Xcode shows an error:
'let' pattern cannot appear nested in an already immutable context
How is a correct way to implement it without any warnings/errors?
The for-in pattern implicitly uses a constant binding (in the scope it creates. That is, your fileData binding is automatically a local let, and therefore constant for each pass through the loop.
So the following:
for fileData in files { /*...*/ }
...is equivalent to :
var index = 0
while index < files.count {
let fileData = files[index]
//...
index += 1
}
You'd want to add var to the for-in binding only when you want to mutate that binding -- that is, if it's an object reference that you want to be able to point at something else during a single pass through the loop, or a value type that you want to be able to change. But it doesn't look like you're doing either of those things, so using var for this binding would be superfluous here.
(Swift 3 got rid of a lot of the places where you could make implicitly immutable bindings mutable, but left for var as an exception — it's still possible if you want to change something during a loop.)

Is Swift type-inference contradicting itself here?

Here's my test code:
var myDict: [String: AnyObject] = ["k":"v"]
var a = myDict["k"]
var b = a as String
var c = myDict["k"] as String
Here's my Swift playground in Xcode6-beta6:
According to the rules of type inference, doesn't complaining about c logically contradict not-complaining about b?
I believe that this is a bug. Part of what is going on here is that String is not an object. If you change the first line to:
var myDict: [String: Any] = ["k":"v"]
then everything is fine. So, given that string is not an object, casting a variable of type AnyObject? to a String should definitely yield an error. And, since the compiler has already decided that a is of type AnyObject? it should complain about casting a to a String.
Note that if you change the last line to:
var c = myDict["k"] as NSString
the error goes away supporting the notion that the issue is that String is not an object. You get the same complaint if you put an Int as the value in the array and try to cast that to an Int.
Update:
So the plot thickens. If you don't import Foundation or import something that imports Foundation, then you get additional errors. Without Foundation:
So clearly some of this has to do with the dual nature of Strings as non-objects and NSStrings as objects and the ability to use Strings as NSStrings when Foundation is imported.
This has to do with the fact that Dictionary has two subscript overloads:
subscript (key: Key) -> Value?
subscript (i: DictionaryIndex<Key, Value>) -> (Key, Value) { get }
The first is the familiar one where you pass a key and it gives you an optional of the value; and you can use to set the value on a key.
The second one is less common. I believe DictionaryIndex is a kind of iterator into the dictionary, and you can use it as a subscript to directly get the key-value pair at that iterator.
When the compiler can't find an overload that matches (in this case, the first one doesn't match because it returns an optional, which cannot be cast to non-optional String), it just picks one arbitrarily (well, it seems arbitrary to me anyway) to show in the error. In this place, it picks the second one, which you don't recognize. That's why the error seems weird to you.
This works.
var c = myDict["k"] as AnyObject! as String // "v"
To answer your question, the reason Swift complains could be that you are trying to do these two conversions in one go. Remember, the statement var a = myDict["k"] contains an implicit conversion already. The implied conversion is AnyObject?, so the above would also work like this:
var c = myDict["k"] as AnyObject? as String // "v"
Note that the above would lead to a run time error if the key "k" where not defined. You would allow this to return nil by casting to String?.

Resources