Python Attr: Converter that uses other attributes? - python-attrs

Is there a way in Python attrs for a converter to reference attributes in self? Right now I'm using __attr_post_init__ but it feels like a sin.
#attrs
class WindowLayout(object):
panel_xxyy_boxes = attrib(type=Dict[str, Tuple[int, int, int, int]])
size = attrib(type=Tuple[int, int], default=None)
def __attrs_post_init__(self):
if self.size is None:
self.size = (max(x for _, x, _, _ in self.panel_xxyy_boxes.values()), max(y for _, _, _, y in self.panel_xxyy_boxes.values()))

I think your “sin” is based on a misconception. :)
“Converting” a missing argument into a computed value is what attrs has defaults for.
Based on your example, something like this should work:
#attrs
class WindowLayout(object):
panel_xxyy_boxes = attrib(type=Dict[str, Tuple[int, int, int, int]])
size = attrib(type=Tuple[int, int])
#size.default
def _size_default(self):
return (max(x for _, x, _, _ in self.panel_xxyy_boxes.values()), max(y for _, _, _, y in self.panel_xxyy_boxes.values()))

Related

Why does an F# Discriminated Union fails to have its TypeConverter respected by JSON.NET but other types can?

#r "../packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.dll"
type [<Struct; System.ComponentModel.TypeConverterAttribute(typeof<CC>)>] C = A of string
and CC() =
inherit System.ComponentModel.TypeConverter()
override _.CanConvertFrom (_, t) = t = typeof<string>
override _.ConvertFrom(_, _, s) = s :?> string |> A |> box<C>
override _.CanConvertTo (_, t) = t = typeof<string>
override _.ConvertTo(_, _, s, _) = s :?> C |> fun (A s) -> s |> box<string>
Newtonsoft.Json.JsonConvert.SerializeObject {|a = A "123"|}
This results in val it : string = "{"a":{"Case":"A","Fields":["123"]}}", which indicates that the TypeConverter is not respected. This also happens for reference DUs.
However, this does not happen with JsonConverters:
#r "../packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.dll"
type [<Struct; Newtonsoft.Json.JsonConverter(typeof<CC>)>] C = A of string
and CC() =
inherit Newtonsoft.Json.JsonConverter()
override _.CanConvert t = t = typeof<string>
override _.ReadJson (r, _, _, _) = r.ReadAsString() |> A |> box<C>
override _.WriteJson (w, v, _) = v :?> C |> fun (A s) -> s |> w.WriteValue
Newtonsoft.Json.JsonConvert.SerializeObject {|a = A "123"|}
This results in val it : string = "{"a":"123"}".
Compare this with records:
#r "../packages/Newtonsoft.Json.12.0.3/lib/netstandard2.0/Newtonsoft.Json.dll"
type [<Struct; System.ComponentModel.TypeConverterAttribute(typeof<CC>)>] C = { A: string }
and CC() =
inherit System.ComponentModel.TypeConverter()
override _.CanConvertFrom (_, t) = t = typeof<string>
override _.ConvertFrom(_, _, s) = { A = s :?> string } |> box<C>
override _.CanConvertTo (_, t) = t = typeof<string>
override _.ConvertTo(_, _, s, _) = (s :?> C).A |> box<string>
Newtonsoft.Json.JsonConvert.SerializeObject {|a = { A = "123"}|}
This also results in val it : string = "{"a":"123"}", which indicates that the TypeConverter is respected.
This shows that something is preventing TypeConverters in discriminated unions from being recognized. What would be the reason? JsonConverters are not usable in dictionary keys, so I would expect TypeConverters to perform better. What would be a viable approach to correctly serialize the aforementioned discriminated union?
Your problem is that Json.NET has its own built-in converter for discriminated unions, DiscriminatedUnionConverter. Any applicable JsonConverter will always supersede an applied TypeConverter.
A built-in converter can be disabled by providing your own, alternate, JsonConverter, either in settings or via an applied JsonConverterAttribute. You have already created a converter that correctly converts your type C, but if you would prefer to fall back to the applied TypeConverter, you can create a JsonConverter that does nothing and falls back on default serialization by returning false from CanRead and CanWrite:
type NoConverter<'a> () =
inherit JsonConverter()
override this.CanConvert(t) = (t = typedefof<'a>)
override this.CanRead = false
override this.CanWrite = false
override this.WriteJson(_, _, _) = raise (NotImplementedException());
override this.ReadJson(_, _, _, _) = raise (NotImplementedException());
Then apply it to your type as follows (demo fiddle #1 here):
type [<JsonConverterAttribute(typeof<NoConverter<C>>); System.ComponentModel.TypeConverterAttribute(typeof<CC>)>] C = A of string
and CC() =
inherit System.ComponentModel.TypeConverter()
override this.CanConvertFrom (_, t) = (t = typeof<string>)
override this.ConvertFrom(_, _, s) = s :?> string |> A |> box<C>
override this.CanConvertTo (_, t) = t = typeof<string>
override this.ConvertTo(_, _, s, _) = s :?> C |> fun (A s) -> s |> box<string>
printfn "%s" (Newtonsoft.Json.JsonConvert.SerializeObject(A "123"))
Or, use it in settings as follows (demo fiddle #2 here):
let settings = JsonSerializerSettings(Converters = [|NoConverter<C>()|])
printfn "%s" (Newtonsoft.Json.JsonConvert.SerializeObject(A "123", settings))

Function string => record member in erlang

I'm wondering how can i define function, which as an argument takes a string and returns a member of the record.
For example with the record
-record(measurement, { temperature, pm2p5, pm10, pressure, humidity, others=[]}).
And the fragment of my function:
update_measurement(Measurement, Type_as_String, Value) ->
Measurement#measurement{get_type(Type_as_String) = Value}
I want to update a value by passing a type as string, and I don't have an idea to define the function get_type(Type_as_String).
I've tried with atoms, but it didn't work.
Something like
update_measurement(Measurement, Type_as_String, Value) ->
case Type_as_String of
"temperature" -> Measurement#measurement{temperature = Value};
"humidity" -> Measurement#measurement{humidity = Value};
...
isn't ok, because i want to reuse this pattern in other functions.
If performance is not your biggest concern:
update_measurement(Measurement, Type_as_String, Value) ->
update_field(Measurement, Type_as_String, Value).
update_field(#measurement{} = Record, SKey, Value) ->
update_field(Record, SKey, Value, record_info(fields, measurement));
% add other records here
update_field(_, _, _) -> error(bad_record).
update_field(Record, SKey, Value, Fields) ->
update_field(Record, list_to_existing_atom(SKey), Value, Fields, 2).
update_field(Record, Key, Value, [Key|_], N) ->
setelement(N, Record, Value);
update_field(Record, Key, Value, [_|Fields], N) ->
update_field(Record, Key, Value, Fields, N+1);
update_field(_, _, _, [], _) ->
error(bad_key).
Note record_info/2 is not a real function but you have to provide measurement as a compile time constant.

Type inference workaround for generic function

Given the following parametric type
type SomeDU2<'a,'b> =
| One of 'a
| Two of 'a * 'b
I have to functions that check if the given param is the respective union case without regard to params
let checkOne x =
match x with
| One _ -> true
| _ -> false
let checkTwo x =
match x with
| Two _ -> true
| _ -> false
This works pretty nice and as expected
let oi = checkOne (One 1)
let os = checkOne (One "1")
let tis = checkTwo (Two (1, "1"))
let tsi = checkTwo (Two ("1", 1))
I can switch the types as I like.
Now However I like to combine those two functions into one creation function
let makeUC () = (checkOne, checkTwo)
and then instantiate like this
let (o,t) = makeUC ()
only it gives me this error message now
Value restriction. The value 'o' has been inferred to have generic type
val o : (SomeDU2<'_a,'_b> -> bool)
Either make the arguments to 'o' explicit or, if you do not intend for it to be generic, add a type annotation.
val o : (SomeDU2<obj,obj> -> bool)
Actually I dont want that - nor do I need that.
Probably its a instance of missing higher kinded types in F#
Is there a way around this?
Edit
Actually me question wasnt complety as per #johns comment below.
Obviously I can do the following
let ro1 = o ((One 1) : SomeDU2<int,int>)
let rt1 = t (Two (1,2))
which then will backwards infer o and t to be of type SomeDU2<int,int> -> bool (I find this backwards inference very strange thou). The problem then is that o wont allow for the below anymore.
let ro2 = o ((One "1") : SomeDU2<string,int>)
So I'd have to instantiate a specific o instance for every combination of generic parameters of SomeDU2.
You would run into the value restriction even without the tuple:
let o = (fun () -> checkOne)()
If you need the results of invoking a function to be applicable to values of any type, then one solution would be to create instances of a nominal type with a generic method:
type DU2Checker =
abstract Check : SomeDU2<'a,'b> -> bool
let checkOne = {
new DU2Checker with
member this.Check(x) =
match x with
| One _ -> true
| _ -> false }
let checkTwo = {
new DU2Checker with
member this.Check(x) =
match x with
| Two _ -> true
| _ -> false }
let makeUC() = checkOne, checkTwo
let o,t = makeUC()
let false = o.Check(Two(3,4))

F# Type mismatch on curried functions

I'm having a bit of trouble with the following FSharp/F# code:
module File1
let api a =
printf ("VALUE = %A") a
let router ops =
[|
api (ops (fun (list, _) -> list()))
api (ops (fun (_, get) -> get 1))
|]
let withContext ops handler =
let context = "CONTEXT"
handler (ops context)
let operations context =
printf ("CONTEXT = %s") context
let list () = [|1;2;3|]
let get id = "Test"
(list, get)
let setup() =
let ops = withContext operations
router ops
Results in the following error
Results in the following compation error
Error 1 Type mismatch. Expecting a
((unit -> int []) * (int -> int []) -> int []) -> 'a
but given a
((unit -> int []) * (int -> string) -> 'b) -> 'b
The type 'int []' does not match the type 'string'
I know the problem is that ops function has been bound to return a int[] but I want to be able to also return a string.
I think I'm missing a trick with some generic declarations but after hours of moving code around I can’t seem to work it out.
(I've simplified the code to highlight my problem)
The error is because ops needs to have a return type of its handler resolved at compilation, and you want to return different types base on some run-time logic.
It is basically an equivalent of:
let fun1 switch arg2 arg3 =
if switch then
arg2
else
arg3
and you want to run it this way:
fun1 true 1 "string"
Of course, arg2 and arg3 need to have the same type, so it won't work
What you can do is to run "api" function on a handler result, before returning it (so it will always the same type - unit).
let router ops =
[|
ops (fun (list, _) -> api <| list()))
ops (fun (_, get) -> api <| get 1))
|]
Alternatively, you could return objects of discriminated union type (then you will need some more logic in api function).
(Technically, you could also return obj).
Bonus
You don't need the array of units to be returned in a router function, returning one unit is just fine:
let router ops =
ops (fun (list, _) -> api <| list()))
ops (fun (_, get) -> api <| get 1))
In this way, setup function will also return unit and you will be able to run it without a need to run ignore on the result to get rid of This expression should have type 'unit', but has type 'unit[]' warning.
Your code is hard for me to understand, but I think the basic issue is that you want withContext to have a "rank 2" type (so that the universal quantification of the type variable 'b can happen after the application of the first argument). In F#, this can be accomplished by creating a new type with a generic method and using that:
let api a =
printf ("VALUE = %A") a
type Handler<'a> = abstract Handle<'b> : f:('a->'b) -> 'b
let router (ops:Handler<_>) =
[|
api (ops.Handle (fun (list, _) -> list()))
api (ops.Handle (fun (_, get) -> get 1))
|]
let withContext ops =
let context = "CONTEXT"
{ new Handler<_> with member __.Handle f = f (ops context) }
let operations context =
printf ("CONTEXT = %s") context
let list () = [|1;2;3|]
let get id = "Test"
(list, get)
let setup() =
let ops = withContext operations
router ops

Sort list of list erlang

PropertyInfo = [
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
]
PropertyInfo is a list of lists containing database objects in tuple, where Heir:code() will return a 6-digit code eg. "010011", "00209", ""020011".
How can I sort this list in erlang by using that Heir code?
By using lists:sort/2 and an ordering function:
manual excerpt:
sort(Fun, List1) -> List2
Types:
Fun = fun((A :: T, B :: T) -> boolean())
List1 = List2 = [T]
T => term()
Returns a list containing the sorted elements of List1,
according to the ordering function Fun. Fun(A, B) should return true
if A compares less than or equal to B in the ordering, false
otherwise.
Ordering fun could look like this:
fun([Tuple1],[Tuple2]) ->
Prop1 = element(3,Tuple1);
Prop2 = element(3,Tuple2);
case {Prop1:code(),Prop2:code()} of
{Same,Same} -> true;
{Code1,Code2} -> SomeComparisonFun(Code1,Code2)
end
end
This leaves you to provide a function that can compare those values, once you've decided on a metric that let's you say which value should be greater than some other one.
SortedList = lists:sort(
fun({_, _, A, _, _, _}, {_, _, B, _, _, _}) ->
A:code() =< B:code()
end,
PropertyInfo).
This was very simple, I used this inbuilt lists:sort() function.

Resources