Why NamedTuple of same types are not compatible? - crystal-lang

Why NamedTuple of the same type are not compatible?
alias Data = NamedTuple(
title: String?
)
data : Data = { title: nil } # Explicitly specifying that
# it has ` : Data` type
proc = ->(data : Data){ p data }
proc.call data # And yet it says it's not
# of `Data` type
Error
Error: type must be NamedTuple(title: String | Nil), not NamedTuple(title: Nil)

Your assumption is incorrect: data : Data restricts the type of the local variable to Data (which is an alias for NamedTuple(title: String | Nil)). This type restriction does not affect the value assignment on the right hand side. The literal value {type: nil} is typed as NamedTuple(title: Nil). This type is compatible with the type restriction to Data, so it can be assigned to the variable.
This implicitly covariant type restriction does not apply with proc types. I'm not sure about the reasoning behind that. It seems that this should be able to work. So maybe it just needs to be implemented.
You can however explicitly cast NamedTuple(Nil) to NamedTuple(String | Nil):
alias Data = NamedTuple(
title: String?
)
data = { title: nil }.as(Data) # Cast named tuple to Data
proc = ->(data : Data){ p data }
proc.call data
However, it is usually not a good idea to work with named tuples as regular data types. They are only recommended for very specific use cases such as named arguments. Defining custom structs (for example using the record macro) is a better idea and gives you a more powerful and flexible data type.
I'd implement your example like this:
record Data,
title : String?
data = Data.new(title: nil)
proc = ->(data : Data){ p data }
proc.call data

This is a bug, please report it in the issue tracker. Thank you!

Related

How to extract results from Redshift Data Api Query in go?

I have a sample table with this structure in Redshift:
{
ID Integer (pk)
Description Varchar NULL
}
I use Redshift Data Service to query it (api reference here). The results are of type GetStatementResultOutput which
contains a property Records of type [][]type.Field.
Now, this type.Field is just an interface to group all the possible field types:
// A data value in a column.
//
// The following types satisfy this interface:
// FieldMemberBlobValue
// FieldMemberBooleanValue
// FieldMemberDoubleValue
// FieldMemberIsNull
// FieldMemberLongValue
// FieldMemberStringValue
type Field interface {
isField()
}
Each of those types really have a Value property that returns the value of the cell. The problem is I don't really have access to the underlying types and I have to extract the values somehow. I'm pretty new at golang and figured a way to extract the values:
type ValueExtractor struct {
Value interface{}
}
columnValueBytes, err := json.Marshal(columnValue)
if err != nil {
logger.Err(err)
}
var valueEx ValueExtractor
err = json.Unmarshal(columnValueBytes, &valueEx)
// The value is here: valueEx.Value
but when the value is null a field of this type is used:
// A value that indicates whether the data is NULL.
type FieldMemberIsNull struct {
Value bool
noSmithyDocumentSerde
}
func (*FieldMemberIsNull) isField() {}
So my value extractor gets true instead of NULL.
What would be the appropriate way of achieving what I want?
Thank you!

Does not conform to String protocol SwiftUI Picker View

I have a simple struct that I will build upon. Right now it has one field, an Int.
struct Card: CustomStringConvertible {
let value: Int
init(value: Int) {
self.value = value
}
var description: String {
return "\(String(value))"
}
}
If I do this, I get the Card to print it's value
let c = Card(value: 1)
print(c)
Now if I put an array of Cards in a CardController like this:
class CardController: ObservableObject {
#Published
var cards: [Card] = [
Card(value: 1),
Card(value: 2),
Card(value: 3)
]
Picker(selection: $selectedCardValue, label: Text("Choose a card")) {
ForEach(0..<cardController.cards.count) {
Text(self.cardController.cards[$0])
}
}
Text("You selected \(selectedCardValue)")
I'll get the error Initializer 'init(_:)' requires that 'Card' conform to StringProtocol. I'm not sure why I get this error. If I instead just change the cards to a type of [String] and values ["1", "2", "3"], the code works fine.
Any idea what's wrong here?
As E.Coms noted, the solution is to use one of the following:
Text(self.cardController.cards[$0].description)
Text(String(describing: self.cardController.cards[$0]))
Here's an explanation of why you have to do this inside the Text initializer, but not print().
Look at the two initializers for Text:
init(verbatim content: String) (docs)
init<S>(_ content: S) where S : StringProtocol (docs)
You must pass either a String or a Substring, the only two types conforming to StringProtocol. In this case, even though your type conforms to CustomStringConvertible, you are still passing a Card.
Contrast this with something like the print function:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") (docs)
Notice that the print function's arguments are denoted by Any, which is explained as
Any can represent an instance of any type at all, including function types.
The print function then converts whatever you passed it to a String:
The textual representation for each item is the same as that obtained by calling String(item).
String has an initializer which takes a type conforming to CustomStringConvertible and returns the description property.
So the reason you can write print(Card()) and not Text(Card() is because the print function has an intermediate step through String that can understand your conformance to CustomStringConvertible, but Text does not. If Text allowed you to pass it any type, it would be both more ambiguous ("What is the text representation of this type?" is not necessarily immediately apparent, as it depends on a hierarchical set of protocols), and more work for the SwiftUI system, which is already doing a lot.
You may miss the description by chance.
ForEach(0..<cardController.cards.count) {
Text(self.cardController.cards[$0].description)
}

How to write reasonml binding for a union type

I am trying to write bindings for https://github.com/oblador/react-native-keychain/blob/master/typings/react-native-keychain.d.ts#L76
getGenericPassword returns false if an error, else an object (credentials). I am not sure this union type can be represented in reason, but a better API would be the result an option (option(credentials)). But, how can I convert Promise<boolean | credentials> -> Js.Promise.t(option(credentials)) in the binding file. Below is a template.
Thanks for your help.
[#bs.deriving abstract]
type credentials = {
service: string,
username: string,
password: string,
};
/* TODO convert the actual return value
Js.Promise.t(option(credentials)) to more reason type
Js.Promise.t(option(credentials)) */
[#bs.module "react-native-keychain"] [#bs.scope "default"]
external getGenericPassword: unit => Js.Promise.t(option(credentials)) = "";
You can use Js.Types.classify to get the runtime type of a value.
type maybeCredentials;
[#bs.module "react-native-keychain"] [#bs.scope "default"]
external getGenericPassword: unit => Js.Promise.t(maybeCredentials) = "";
let getGenericPassword: unit => Js.Promise.t(option(credentials)) =
() =>
Js.Promise.(
getGenericPassword()
|> then_(maybeCredentials =>
switch (Js.Types.classify(maybeCredentials)) {
| JSObject(obj) => resolve(Some(obj |> Obj.magic))
| _ => resolve(None)
}
)
);
Here maybeCredentials is defined and used as an intermediate type.
We then define a function with the same name as the binding, which will "shadow" the name and prevent the binding from being used directly in favour of our "override". However, within the override we're still able to use the binding.
We then call Js.Types.classify to get the runtime type of the returned value. If it is an object we use Obj.magic to cast the abstract obj_type to our credentials type (inferred from the return type of the function), and wrap it in an option. For any other type we return None.
By the way, this kind of "type" is called an untagged union. I've written down a few examples using different strategies for dealing with these, as both a producer and a consumer, in bucklescript-cookbook.

reasonml record vs JS object

Say I define the following types:
type queueParams = {
durable: bool
};
class type amqpChannelT = [#bs] {
pub assertQueue: string => queueParams => Js.Promise.t(unit);
};
Then calling the following:
channel##assertQueue("exampleQueue", {"durable": bool});
Results in:
This has type:
{. "durable": bool}
But somewhere wanted:
queueParams (defined as
How can I pass the right type of thing? Why is the thing I'm passing not a record? And what is the meaning of that dot notation?
ReasonML interprets these two differently:
let jsObject = {"durable": true};
let reasonRecord = {durable: true};
Basically, wrapping keys with double-quotes is a short-hand notation for the special Javascript object type Js.t('a) - which is currently deprecated.
You can play around with an example here. Note how the two types are treated differently when converted to Javascript.
Read more about the deprecated syntax here:
https://bucklescript.github.io/docs/en/object-deprecated

Array of tuples in Swift

I have a function:
func parseJSON3(inputData: NSData) -> NSArray {
var tempDict: (id:Int, ccomments:Int, post_date:String, post_title:String, url:String) = (id: 0, ccomments: 0, post_date: "null", post_title: "null", url: "null")
var resultArray: (id:Int, ccomments:Int, post_date:String, post_title:String, url:String)[] = []
var error: NSError?
var jsonDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(inputData, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSDictionary
var firstArray = jsonDictionary.objectForKey("locations") as NSArray
for dict in firstArray {
tempDict.id = dict.valueForKey("ID") as Int
tempDict.ccomments = dict.valueForKey("ccomments") as Int
tempDict.post_date = dict.valueForKey("post_date") as String
tempDict.post_title = dict.valueForKey("post_title") as String
tempDict.url = dict.valueForKey("url") as String
resultArray.append(tempDict)
}
return resultArray
}
In line
resultArray.append(tempDict)
I have an error:
Missing argument for parameter 'ccomments' in call
Why? Help please....
It looks to me like resultArray.append() is treating the tuple a little bit like a variadic parameter, and trying to expand the tuple to match its own arguments. It's complaining about your second parameter because it's only expecting one. I haven't seen this behavior for Array.append() documented anywhere, so I would say it's a bug in Swift.
Using the appending operator += doesn't seem to have that issue:
resultArray += tempDict
So this is pretty wild - not sure if I would qualify it as a bug or as undocumented behavior, but it's definitely something that should be on the radar for a fix / clarification!
The situation is that append is treating your argument tempDict (which we would expect to be the only argument to an Array method that takes a single member and adds it to the collection) as the first argument in a signature where it is looking for 5 arguments (!), one for each member of the Tuple type that the Array holds.
See the following for some interesting behavior (including assigning a label to the single member of a 1-member 'Tuple' ??) ->
var arrayOne: Array<String> = []
arrayOne.append("hi")
println(arrayOne[0]) // hi
var arrayTwo: Array<(String)> = [] // where (String) is a single-member Tuple
arrayTwo.append("hi")
println(arrayTwo[0]) // hi
println(arrayTwo[0].0) // hi -> using .0 subscript to access the first member of the Tuple
// wanna see something crazy? remember arrayOne, that holds members of type String?
println(arrayOne[0].0) // hi -> this Array does not hold Tuples, but it looks like we can still treat its members like "single-member Tuples"?
var arrayThree: Array<(str: String)> = [] // members of the Array are single-member Tuples with the label 'str' for their member
arrayThree.append(str: "hi") // now we can't use append without providing the label 'str', acting as what looks like an argument label?
var byeString = "bye"
var byeTuple = ("bye")
arrayThree += byeString // += doesn't care about the label, and will take either a String or a single-member Tuple holding a String
arrayThree += byeTuple
println(arrayThree[0]) // hi
println(arrayThree[0].0) // hi
println(arrayThree[0].str) // hi -> accessing the single member of the Tuple by its label
...so in your case, where you are seeing the error with append what it wants you to do is (using the labels you used to declare the Tuple as something that looks like argument labels):
var resultArray: (id:Int, ccomments:Int, post_date:String, post_title:String, url:String)[] = []
...
resultArray.append(id: someIntValue, ccomments: someOtherIntValue, post_date: someStringValue, post_title: someOtherStringValue, url: someAnotherStringValue)
...and of course, as discussed, you can avoid doing that by just using += instead
Crazy stuff! could be by design to serve some purpose, could be a consequence of protocol inheritance that wasn't meant to have this effect... would be interesting to know the answer!
resultArray.append() seems to be taking tempDict as the first tuple element (id).
Changing it to :
resultArray += tempDict
seems to compile and work.
I'm not sure why append() doesn't behave the same way, maybe you can file a bug!