How to create Map of tuples in ReasonML? - dictionary

I'm very new to Reason. I have a tuple containing two strings and want to make a Map where the keys are of that tuple type.
How should I go about doing it?

Map.Make is a functor, which means it expects a module as its argument, not a type. The module argument must conform to the OrderedType signature:
module type OrderedType = {
type t
let compare : (t, t) => int
}
In your case that would be something like:
module TuplesMap = Map.Make({
type t = (string, string)
let compare = (a, b) => ...
});
Then all you need to do is to implement the compare function.

Related

Cover different types of values in Vector

I want to handle different types of value I get via [] or get() methods of Vec.
But somehow it throws me an error.
I meant I should be able to cover all the occurrences(Types) in the Vec.
In the example, d_vec[0] is of Type Spreadsheet::Text(String). It always shows me that I need String as expected value, but that can be, that I won't know the exact type (out of these 3 Spreadsheet) of the value I'm trying to get via [] or get().
Code:
#[derive(Debug)]
enum Spreadsheet {
Text(String),
Float(f32),
Int(i32),
}
let mut d_vec: Vec<Spreadsheet> = Vec::new();
d_vec.push(Spreadsheet::Text(String::from("SOME TEXT")));
d_vec.push(Spreadsheet::Float(5.0f32));
d_vec.push(Spreadsheet::Int(10i32));
let b: Spreadsheet = d_vec[0];
// Catch values of different types
match b {
Spreadsheet::Text(v) => v,
Spreadsheet::Float(v) => v,
Spreadsheet::Int(v) => v,
}
It suggests me to use conversion via .to_string(), but right after the conversion it throws an error about expected () instead of String
Before using to_string
After using to_string
Will appreciate any help, because It's driving me crazy :(

How do I specify a type for a function parameter that optionally includes a given method?

Updated Question
I want to define a function named bsearch() to do binary searches against arrays of arbitrary object types. When I invoke the function, I want it to check whether or not the Type of the array contains a compare() method and use it, if it does. If it does not, I want it to fall back to using < and === (so it will work with strings and numbers).
What should the function declaration look like? (I don't need an actual implementation, just the syntax for a type-safe solution.)
Or maybe I'm going about this all wrong? How can I create a function that uses a method built into a parameter type if it exists, or use some other function when it doesn't?
Original Question
This is the original question, but I've replaced it with the above as it seems this wasn't getting my point across.
I want to define a function named bsearch() to do binary searches against arrays of arbitrary object types. So I'd like to do something like this:
type Comparator = <Type>(a: Type, b: Type) => -1 | 0 | 1;
static bsearch<Type extends { compare?: Comparator }>(
ary: Type[],
value: Type
): number { ... }
My goal is to specify that Type must extend a type that may or may not include the compare method. In my function, I will check whether the compare method exists on the value parameter and call if it does, or use a generic function (that uses < and ===) if it does not.
The definition of bsearch() does not produce any warnings or errors, but attempts to invoke it from my unit test does:
class Person {
name: string;
length: number;
compare: Comparator<Person>; // What goes here?
}
describe('Utils tests', () => {
const arrayOfInt = [10, 20, 30, 40];
const arrayOfStr = ['Alfred', 'Bob', 'Chuck'];
const arrayOfPersons: Person = [
{name:'Barney',length:2},
{name:'Fred',length:6}
{name:'Wilma',length:12},
];
it('can find integer in an array of integers', () => {
let search_for = 30;
let result = Utils.bsearch(arrayOfInt, search_for)
expect(result).to.be.equal(2);
});
it('can find string in an array of strings', () => {
let search_for = 'Bob';
let result = Utils.bsearch(arrayOfStr, search_for)
expect(result).to.be.equal(1);
});
it('can find Person in an array of Persons', () => {
// This one uses Person.compare() to do the search.
// The previous two tests used the fallback technique.
let search_for = {name:'Fred',length:6};
let result = Utils.bsearch(arrayOfPersons, search_for)
expect(result).to.be.equal(1);
});
});
The error message is:
TS2345: Argument of type 'number[]' is not assignable to parameter of type '{ compare?: Comparator | undefined; }[]'.   Type 'number' has no properties in common with type '{ compare?: Comparator | undefined; }'.
I would appreciate pointers to other techniques if there is a better way to accomplish this (I'm still a TypeScript newbie).
Your generic is:
Type extends { compare?: Comparator }
Which means that Type must fulfill { compare?: Comparator } type. While passing object value, for example { name: 'Barney', length: 2, comparator: /* snip */}, is obviously correct, it's not the case for primitives like 10 and Bob. You need to include information about primitive types in the generic, for example:
Type extends ({ compare?: Comparator }) | number | string
Also, you'd probably want to enrich a bit the object typing:
{[key: string]: unknown, compare?: () => void } | number | string
Because, based on your description, you'd also want to accept also objects that do not have compare function in their type signature at all. If it does sound strange, I recommend reading about excess property checking.

Convert to readonly collection with AutoMapper

I need to find a way to convert list of arbitrary values to another list
AutoMapper works if destination type is ICollection<> because it's creating an instance and populating it with Add, but my type is immutable list 'a list
So if I create list of ints:
let ints = [1; 2; 3]
And try to map it to ResizeArray<int64> (synonym to List<T>) with
mapper.Map<ResizeArray<int64>>(ints)
it will work, but if I try to map it to int64 list with
mapper.Map<int64 list>
then it will fail.
I've found a solution that will convert successfully, but it will work only with explicitly defined types
let cfg = MapperConfiguration(
fun c ->
c.CreateMap<int, int64>() |> ignore
c.CreateMap<int list, int64 list>()
.ConvertUsing(
fun source _ (cfg: ResolutionContext) ->
source
|> Seq.map cfg.Mapper.Map<int, int64>
|> Seq.toList))
So question is: How to write type converter that will convert 'a list to 'b list without explicitly defining all possible combinations of these types?
I've finally found solution. All I needed is to look at source code of this ReadOnlyCollection mapper
Solution is not perfect, because collection items transformed and inserted into System.Collections.Generic.List and afterwards converted to Microsoft.FSharp.Collections.FSharpList, which have some overhead. But at least it's working
using System.Collections.Generic;
using System.Linq.Expressions;
using static System.Linq.Expressions.Expression;
using AutoMapper.Mappers;
using AutoMapper.Internal;
using static AutoMapper.Internal.CollectionMapperExpressionFactory;
using Microsoft.FSharp.Collections;
public class SeqToFSharpListMapper : EnumerableMapperBase
{
public override bool IsMatch(TypePair context)
=> context.SourceType.IsEnumerableType()
&& context.DestinationType.FullName.StartsWith("Microsoft.FSharp.Collections.FSharpList`1");
public override Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, IMemberMap memberMap,
Expression sourceExpression, Expression destExpression, Expression contextExpression)
{
var listType = typeof(List<>).MakeGenericType(ElementTypeHelper.GetElementType(destExpression.Type));
var list = MapCollectionExpression(configurationProvider, profileMap, memberMap, sourceExpression, Default(listType), contextExpression, typeof(List<>), MapItemExpr);
return Call(typeof(ListModule).GetMethod(nameof(ListModule.OfSeq)).MakeGenericMethod(destExpression.Type.GenericTypeArguments[0]), list);
}
}
And F#
override _.MapExpression (configurationProvider, profileMap, memberMap, sourceExpression, destExpression, contextExpression) =
let listType = typedefof<System.Collections.Generic.List<_>>.MakeGenericType(ElementTypeHelper.GetElementType destExpression.Type)
let list = MapCollectionExpression(configurationProvider, profileMap, memberMap,
sourceExpression, Default(listType), contextExpression,
typedefof<System.Collections.Generic.List<_>>,
MapItem(fun c p s d ctx i -> MapItemExpr(c, p, s, d, ctx, &i))) // compiler require explicit lambda
upcast Call(typedefof<obj list>.Assembly // don't want to use AssemblyQualifiedName
.GetType("Microsoft.FSharp.Collections.ListModule") // have to use this trick because we can't access ListModule through typeof
.GetMethod("OfSeq")
.MakeGenericMethod(destExpression.Type.GenericTypeArguments.[0]),
list)

What does the intersection of two function types boil down to?

Can someone point me to a comprehensive guide on the theory behind flowtype function intersections? Behavior is confusing to me. I understand that this type:
type FnT = ((string) => string) & ((number) => string);
reduces down to (string | number) => (string & string), but why is is that i can't cast the parameter to either string or number ???
i.e const g: FnT = (p: string) => { return "hi"; } gives me
Cannot assign function togbecause string [1] is incompatible with number [2] in the first argument..
Why??? isn't string a perfectly valid subtype of string | number?
is this because it expects a super type?
if this is the case then why is it that a union of same two function types lets me cast the param to one or the other?
i.e.
const FnT = ((string) => string) | ((number) => string) works with
const g: FnT = (p: string) => ("hi") ??? wouldn't we expect a supertype of string | number here?
With flow, you need to test all alternative types before casting.
example, if your type is string|number, and you want to cast as a number, you must first test that it is not actually a string.
This is because Flow will not try and modify your values for you, it is only a type checker. You must modify your values yourself, meaning flow can not 'convert' a number to a string, it can only cast the type.

type-checking function signatures with less or more arguments

This is about Flow typechecking pronouncements when a function type is defined and the function expression we're trying to typecheck has: (a) more or (b) fewer arguments than the defined type.
The following typechecks as it should, no questions here.
declare type TFunctionNumberToBoolean = (n: number) => boolean;
const f: TFunctionNumberToBoolean = function isEven(n: number) {return n%2==0;}
The following case (case A) where the normative "number to boolean" function type is changed as follows:
(n: number) => boolean
… doesn't typecheck:
declare type TFunctionNumberToBoolean = () => boolean;
const f: TFunctionNumberToBoolean = function isEven(n: number) {return n%2==0;}
The following case (case B) where the normative "number to boolean" function type is changed as follows:
(n: number, foo :number) => boolean
… typechecks:
declare type TFunctionNumberToBoolean = (n: number, foo: number) => boolean;
const f: TFunctionNumberToBoolean = function isEven(n: number) {return n%2==0;}
What is the reasoning behind Case A (more arguments than the defined function type) not typechecking but Case B (less arguments than the defined function type) typechecking ? I naively think that an argument could be made for the opposite behavior to have been more intuitive.
I am using Flow 0.35.
Let's say you have a function that takes two properties of an object and sums them:
function sum(obj) {
return obj.a + obj.b;
}
What happens if you pass additional arguments? Nothing bad, it will be ignored.
What happens if you pass no arguments? You'll get TypeError: Cannot read property 'a' of undefined.
Passing more arguments is safe, passing less arguments is unsafe.

Resources