I've been messing about with F# and it's Reflection, trying to create a Record type object dynamically from within F#, I got most of it working (as you can see below) but one thing - the record I create through reflection has type "obj" instead the one it should ("Person") and I can't seem to be able to upcast it in any way.
#light
type Person = {
Name:string;
Age:int;
}
let example = {Name = "Fredrik"; Age = 23;}
// example has type Person = {Name = "Fredrik"; Age = 23;}
let creator = Reflection.FSharpValue.PrecomputeRecordConstructor(example.GetType(),
System.Reflection.BindingFlags.Public)
let reflected = creator [| ("thr" :> obj); (23 :> obj) |]
// here reflected will have the type obj = {Name = "thr"; Age = 23;}
// Function that changes the name of a Person record
let changeName (x:Person) (name:string) =
{ x with Name = name }
// Works with "example" which is has type "Person"
changeName example "Johan"
// But not with "reflected" since it has type "obj"
changeName reflected "Jack" // Error "This expression has type obj but is here used with type Person. "
// But casting reflected to Person doesn't work either
(reflected :> Person) // Type constraint mismatch. The type obj is not compatible with
// type Person. The type 'obj' is not compatible with the type 'Person'.
// C:\Users\thr\Documents\Visual Studio 2008\Projects\
// Reflection\Reflection\Script.fsx 34 2 Reflection
Try using the other cast operator (as you're casting the other way this time)
So changeName (reflected :?> Person) "Jack"
Related
I am trying to dereference a channel and use the payload in the channel to check if there is an entry in the map or not.
If the map is of not type it works but if the map has a type I always get the error that type <map> is not an expression.
type writechan chan UserProfile
type usersmap map[string]UserProfile
var myuserprofile UserProfile
myuserprofile = <-myuserchannel
var username = myuserprofile.name
if _, ok := usersmap[username]; ok {
fmt.Println("Already registered ")
} else {
usersmap[username] = myuserprofile
}
The error message says type usersmap is not an expression" in the if line . If I don't use the type usermap but a normal map say map[string]string it works fine.
type usersmap map[string]UserProfile
This is a type declaration, it does not create a variable named usersmap. After this type declaration you may use the usersmap (type) identifier to create a variable like this:
var umap usersmap
And to initialize it:
umap = make(usersmap)
Or in one line:
var umap = make(usersmap)
Or using a short variable declaration with initialization:
umap := usersmap{}
Also to create a variable of map type you don't necessarily have to create a new type, you may simply write:
var umap = make(map[string]UserProfile)
I have some code like this that works fine:
type App() =
inherit Application()
let stack = StackLayout(VerticalOptions = LayoutOptions.Center)
let label = Label(XAlign = TextAlignment.Center, Text = "Welcome to F# Xamarin.Forms!")
do
let tapRecognizer = new TapGestureRecognizer()
let handleTapEvent (sender:Object) (args:EventArgs) =
label.Text <- "Tapped at " + DateTime.Now.ToString()
()
let tapEventHandler = new EventHandler(handleTapEvent)
tapRecognizer.Tapped.AddHandler(tapEventHandler)
label.GestureRecognizers.Add(tapRecognizer)
However when I change the args from EventArgs to a derived type like this:
type TapEventArgs(someId:int) =
inherit EventArgs()
member this.SomeId = someId
let handleTapEvent (sender:Object) (args:TapEventArgs) =
label.Text <- args.SomeId.ToString() + " tapped"
()
I get the following error when I call AddHandler
The type 'EventArgs' is not compatible with the type 'TapEventArgs'
Also, If I change the EventHandler like this:
let tapEventHandler = new EventHandler<TapEventArgs>(handleTapEvent)
I get this error
This expression was expected to have type 'EventHandler'
but here has type 'EventHandler<TapEventArgs>'
Any way to force that derived type?
F# does not insert downcasts automatically the way C# does (and this is a good thing, not a bug). You cannot pass a descendant type where an ancestor type is expected.
In order to call AddHandler you need to insert a downcast manually using the downcast operator :>, like this:
tapRecognizer.Tapped.AddHandler(tapEventHandler :> EventHandler<EventArgs>)
When the target type is already known (like in your case), you can use an underscore in its place to let the F# compiler infer it from the context:
tapRecognizer.Tapped.AddHandler(tapEventHandler :> _)
The problem here is that the types are different, and that is what F# is telling you. The type of tapRecognizer.Tapped is of EventHandler, so when it is invoked it will be of type EventHandler ie not EventHandler<TapEventArgs> which is a different type. There is no way to change this, and subclassing TapGestureRecognizer is also not possible as the class is sealed.
Additionally, the code you posted would be difficult to compile, as it has circular references which F# prevents. TapEventArgs requires label which is defined in App. App depends on TapEventArgs. It would not be possible to update the UI from TapEventArgs instead a function would need to be passed in, or the state exposed.
There is a solution using the Command pattern, which allows some information to be passed to the callback and avoids circular dependencies. TapGestureRecognizer has a property CommandParameter where a value (of type obj) can be set. A callback can be supplied via the Command property that can receive that value. Here is the full example:
open Xamarin.Forms
open System
type App() as this =
inherit Application()
let stack = StackLayout(VerticalOptions = LayoutOptions.Center)
let label = Label(XAlign = TextAlignment.Center, Text = "Welcome to F# Xamarin.Forms!")
do
let tapRecognizer = new TapGestureRecognizer()
let handleTapEvent (x:obj) =
match x with
| :? int as someId -> label.Text <- someId.ToString() + " tapped"
| _ -> label.Text <- "Tapped at " + DateTime.Now.ToString()
tapRecognizer.Command <- new Command(Action<obj>(fun x -> handleTapEvent x))
label.GestureRecognizers.Add(tapRecognizer)
stack.Children.Add label
tapRecognizer.CommandParameter <- 42 // The value to be passed to the Command's callback
this.MainPage <- ContentPage(Content = stack)
Note that casting must be used, because of the way the types are defined in Xamarin.Forms (using obj for the property).
I want to cast MethodBase to RuntimeMethodInfo in order to retrieve the name and type of the arguments of reflected methods, and the returned type of those methods.
I can make a direct cast in Inmediate Window but have not found a way to make the cast with F#.
Why cast? You can call GetParameters() and all the other members you'd need on the MethodBase reference.
let methodInfo : MethodBase = //whatever
let firstParamName = methodInfo.GetParameters().[0].Name
EDIT (return types):
First, note that GetMethod returns MethodInfo, not MethodBase. You can't cast to RuntimeMethodInfo since, as others have noted, that is an internal type. But the ReturnType property is declared on MethodInfo, so all is well.
This therefore works, with the static type of methodInfo being MethodInfo:
let methodInfo = typeof<object>.GetMethod("ToString")
let returnTypeName = methodInfo.ReturnType.Name // "String"
Second, if you have a static reference to a MethodBase that you know is a MethodInfo, use the :?> operator. Example:
let toMethodInfo (mb : MethodBase) = mb :?> MethodInfo
On the other hand, if you're not sure about the object's actual type, you can use match:
let tryToMethodInfo (mb : MethodBase) =
match mb with
| :? MethodInfo as result -> Some result
| _ -> None
Finally, since you ask about "vice versa" in your comment: When you're going from a derived class to one of its base classes, which always succeeds, you don't need the question mark:
let toMethodBase (mi : MethodInfo) = mi :> MethodBase
I am trying to do this:
var dictArray = [String:[String]]()
dictArray["test"] = [String]()
dictArray["test"]! += "hello"
But I am getting the weird error NSString is not a subtype of 'DictionaryIndex<String, [(String)]>'.
I just want to be able to add objects to an array inside a dictionary.
Update: Looks like Apple considers this a "known issue" in Swift, implying it will work as expected eventually. From the Xcode 6 Beta 4 release notes:
...Similarly, you cannot modify the underlying value of a mutable
optional value, either conditionally or within a force-unwrap:
tableView.sortDescriptors! += NSSortDescriptor(key: "creditName", ascending: true)
Workaround: Test the optional value explicitly and then assign the
result back:
if let window = NSApplication.sharedApplication.mainWindow {
window.title = "Currently experiencing problems"
}
tableView.sortDescriptors = tableView.sortDescriptors!
You can only do this
var dictArray = [String:[String]]()
dictArray["test"] = [String]()
var arr = dictArray["test"]!;
arr += "hello"
dictArray["test"] = arr
because dictArray["test"] give you Optional<[String]> which is immutable
6> var test : [String]? = [String]()
test: [String]? = 0 values
7> test += "hello"
<REPL>:7:1: error: '[String]?' is not identical to 'UInt8'
append also won't work due to the same reason, Optional is immutable
3> dictArray["test"]!.append("hello")
<REPL>:3:18: error: '(String, [(String)])' does not have a member named 'append'
dictArray["test"]!.append("hello")
^ ~~~~~~
BTW the error message is horrible...
You may use NSMutableArray instead of [String] as a value type for your dictionary:
var dictArray: [String: NSMutableArray] = [:]
dictArray["test"] = NSMutableArray()
dictArray["test"]!.addObject("hello")
This is still an issue in Swift 3. At least I was able to create method that can handle it for you.
func appendOrCreate(newValue: Any, toArrayAt key: String, in existingDictionary: inout [AnyHashable:Any]) {
var mutableArray = [Any]()
if let array = existingDictionary[key] as? [Any]{
//include existing values in mutableArray before adding new value
for existingValue in array {
mutableArray.append(existingValue)
}
}
//append new value
mutableArray.append(newValue)
//save updated array in original dictionary
existingDictionary[key] = mutableArray
}
The problem is that we want class semantics here but have to use structs. If you put class objects into the dictionary, you get what you want!
So, if you haveĀ¹ to have mutable values, you can wrap them in a class and perform updates with a closure:
class MutableWrapper<T> {
var rawValue: T
init(_ val: T) {
self.rawValue = val
}
func update(_ with: (inout T) -> Void) {
with(&self.rawValue)
}
}
Example:
func foo() {
var dict = [String: MutableWrapper<[String]>]()
dict["bar"] = MutableWrapper(["rum"])
dict["bar"]?.update({$0.append("gin")})
print(dict["bar"]!.rawValue)
// > ["rum", "gin"]
}
For what it's worth, I do not see a way to keep caller and wrapper in sync. Even if we declare init(_ val: inout T) we will end up with a copy in rawValue.
Performance is not necessarily an issue since the compiler optimizes structs heavily. I'd benchmark any mutable solution against what looks like lots of copy-updates in the code.
Since Swift 4.1 you can provide a default value to the subscript which allows you to solve this quite naturally now:
dictArray["test", default: []].append("hello")
I'm finding some surprising behavior with optional dictionaries in Swift.
var foo:Dictionary<String, String>?
if (foo == nil) {
foo = ["bar": "baz"]
}
else {
// Following line errors with "'Dictionary<String, String>?' does
// not have a member named 'subscript'"
foo["qux"] = "quux"
}
I've played with this a lot, trying to figure out what I might be missing, but nothing seems to make this code work as expected short of making the dictionary not optional. What am I missing?
The closest I can get is the following, but of course it's ridiculous.
var foo:Dictionary<String, String>?
if (foo == nil) {
foo = ["bar": "baz"]
}
else if var foofoo = foo {
foofoo["qux"] = "quux"
foo = foofoo
}
The lightbulb moment is when you realize that an Optional dictionary is not a Dictionary. An Optional anything is not that thing! It is an Optional!! And that's all it is. Optional is itself a type. An Optional is just an enum, wrapping the possible cases nil and some value. The wrapped value is a completely different object, stored inside.
So an Optional anything does not act like the type of that thing. It is not that thing! It is just an Optional. The only way to get at the thing is to unwrap it.
The same is true of an implicitly unwrapped Optional; the difference is just that the implicitly unwrapped Optional is willing to produce (expose) the wrapped value "automatically". But it is still, in fact, wrapped. And, as Bryan Chen has observed, it is wrapped immutably; the Optional is just holding it for you - it is not giving you a place to play with it.
you can use this code
if var foofoo = foo {
foofoo["qux"] = "quux"
foo = foofoo
} else {
foo = ["bar": "baz"]
}
with this code
var foo:Dictionary<String, String>? = Dictionary()
foo[""]=""
error: 'Dictionary<String, String>?' does not have a member named 'subscript'
foo[""]=""
^
the error message makes sense to me that Dictionary<String, String>? does not implement subscript method, so you need to unwrap it before able to use subscript.
one way to call method on optional is use ! i.e. foo![""], but...
var foo:Dictionary<String, String>? = Dictionary()
foo![""]=""
error: could not find member 'subscript'
foo![""]=""
~~~~~~~~^~~
whereas
var foo:Dictionary<String, String>? = Dictionary()
foo![""]
works
it is interesting these code failed to compile
var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo[""]=""
error: could not find an overload for 'subscript' that accepts the supplied arguments
foo[""]=""
~~~~~~~^~~
var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo.updateValue("", forKey: "")
immutable value of type 'Dictionary<String, String>' only has mutating members named 'updateValue'
foo.updateValue("", forKey: "")
^ ~~~~~~~~~~~
the last error message is most interesting, it is saying the Dictionary is immutable, so updateValue(forKey:) (mutating method) can't be called on it
so what happened is probably that the Optional<> store the Dictionary as immutable object (with let). So even Optional<> it is mutable, you can't modify the underlying Dictionary object directly (without reassign the Optional object)
and this code works
class MyDict
{
var dict:Dictionary<String, String> = [:]
subscript(s: String) -> String? {
get {
return dict[s]
}
set {
dict[s] = newValue
}
}
}
var foo:MyDict? = MyDict()
foo!["a"] = "b" // this is how to call subscript of optional object
and this lead me to another question, why Array and Dictionary are value type (struct)? opposite to NSArray and NSDictionary which are reference type (class)
This is because your Dictionary is optional. If it's nil, you won't add an entry to it.
You can do this way:
var dict: [String : String]?
if let dict = dict {
dict["key"] = "value" // add a value to an existing dictionary
} else {
dict = ["key" : "value"] // create a dictionary with this value in it
}
Or, if you are given an optional dictionary, for example HTTPHeaders - which in AlamoFire is a [String : String] dictionary - and you want to either add a value if it's non-nil, or create it with this value if it's nil, you could do like so:
let headers: HTTPHeaders? // this is an input parameter in a function for example
var customHeaders: HTTPHeaders = headers ?? [:] // nil coalescing
customHeaders["key"] = "value"
I tried this for Swift 3.1 and it worked:
if (myDict?[key] = value) == nil {
myDict = [key: value]
}