Erlang server crashing after timeout from timer module - timer

I have a Table module. When it starts as a gen_server, it creates 2 servers from the Clock module - one for the X player and one for the O player.
After 10 seconds the Clock will time out and this code is called:
updateTick(Delta, {{Time, ticking, _}, Host}) ->
Now = Time - Delta,
case Now of
Now when Now > 0 ->
{{Now, ticking, intervalRef()}, Host};
_ ->
gen_server:call(Host, timeout),
{{0, out, none}, Host}
end;
I expect the latter clause to fire.
Here's the crash I get:
Eshell V8.2 (abort with ^G) (tunnel#127.0.0.1)1> Clock.{{1000,paused,none},<0.532.0>} Clock.{{20.823899999999803,ticking,#Ref<0.0.1.603>},<0.532.0>}
=ERROR REPORT==== 4-Sep-2017::20:10:19 ===
** Generic server <0.536.0> terminating
** Last message in was {tick,25.099}
** When Server state == {{20.823899999999803,ticking,#Ref<0.0.1.603>},
<0.532.0>}
** Reason for termination ==
** {{timeout,{gen_server,call,[<0.532.0>,timeout]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,204}]},
{clock,updateTick,2,[{file,"src/clock.erl"},{line,27}]},
{clock,handle_call,3,[{file,"src/clock.erl"},{line,35}]},
{gen_server,try_handle_call,4,[{file,"gen_server.erl"},{line,615}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,647}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]}
The gen_server being called with timeout is a Table server. The Clocks keep a reference to the Table to send the timeout message to them when a clock times out, so the Table can cancel the others and do other logic.
Here is my full clock.erl:
-module(clock).
-compile(export_all).
-define(STARTING, 1000).
-define(INTERVAL, 250).
init([Host]) ->
{ok, defaultState(Host)}.
defaultState(Host) ->
{{?STARTING, paused, none}, Host}.
tickPid(Pid, Then) ->
Delta = timer:now_diff(erlang:timestamp(), Then) / 10000,
s:s(Pid, {tick, Delta}).
intervalRef() ->
{ok, {_, Tref}} = timer:apply_after(?INTERVAL, ?MODULE, tickPid, [self(), erlang:timestamp()]),
Tref.
updateTick(Delta, {{Time, ticking, _}, Host}) ->
Now = Time - Delta,
case Now of
Now when Now > 0 ->
{{Now, ticking, intervalRef()}, Host};
_ ->
s:s(Host, timeout),
{{0, out, none}, Host}
end;
updateTick(_, State) ->
State.
handle_call({tick, Delta}, _, State) ->
State2 = updateTick(Delta, State),
{reply, State2, State2};
handle_call(info, _, State) ->
{reply, State, State};
handle_call(pause, _, {{Time, ticking, Tref}, Host}) ->
timer:cancel(Tref),
State2 = {{Time, paused, none}, Host},
{reply, State2, State2};
handle_call(start, _, {{Time, paused, _}, Host}) ->
{ok, Tref} = timer:apply_after(?INTERVAL, ?MODULE, tickPid, [self(), erlang:timestamp()]),
State2 = {{Time, ticking, Tref}, Host},
{reply, State2, State2};
handle_call(stop, _From, State) ->
{stop, normal, shutdown_ok, State};
handle_call(_, _, State) ->
{reply, State, State}.
terminate(_, State = {{_,_, none}, _}) ->
io:format("Clock.~p~n", [State]),
ok;
terminate(_, State = {{_,_,Tref}, _}) ->
timer:cancel(Tref),
io:format("Clock.~p~n", [State]),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
handle_cast(_, State) ->
{noreply, State}.
handle_info(Msg, State) ->
io:format("Unexpected message: ~p~n",[Msg]),
{noreply, State }.
go(Host) ->
gen_server:start_link(?MODULE, [Host], []).
and full table.erl:
-module(table).
-compile(export_all).
-behavior(gen_server).
-define(POSTGAME_TIMEOUT, 6000).
otherPlayer(x) -> o;
otherPlayer(o) -> x.
processRecentTaken(true) -> 1;
processRecentTaken(false) -> 0.
processResult({error, Error, _Board}, State) ->
{{error, Error}, State};
processResult({playing, NewBoard, Slices, RecentTaken}, {Board, Status, Players}) ->
% update recent taken
CurrentPlayer = maps:get(current_player, Status),
OtherPlayer = otherPlayer(CurrentPlayer),
{OClock, OActions} = maps:get(OtherPlayer, Players),
s:s(OActions, {recent_bonus, processRecentTaken(RecentTaken)}),
% update slice bonus
{CClock, CActions} = maps:get(CurrentPlayer, Players),
Made = s:s(CActions, {made, Slices}),
Status2 = case Made of
{over, _} ->
s:s(Board, {cycle, OtherPlayer}),
s:s(CClock, pause),
s:s(OClock, start),
maps:put(current_player, OtherPlayer, Status);
_ -> Status
end,
{{ok, NewBoard}, {Board, Status2, Players}};
processResult({Win, NewBoard, _Slices, _RecentTaken}, State) ->
{{ok, NewBoard}, winGame(Win, State)}.
markAsFinished(Pid, _Timestamp) ->
s:s(Pid, finished).
winGame(x, State) ->
winGame(xWin, State);
winGame(o, State) ->
winGame(oWin, State);
winGame(Win, {Board, Status, Players}) ->
CurrentPlayer = maps:get(current_player, Status),
OtherPlayer = otherPlayer(CurrentPlayer),
{OClock, _} = maps:get(OtherPlayer, Players),
CurrentPlayer = maps:get(current_player, Status),
{CClock, _} = maps:get(CurrentPlayer, Players),
s:s(OClock, pause),
s:s(CClock, pause),
Status2 = maps:put(result, Win, Status),
{ok, _Tref} = timer:apply_after(?POSTGAME_TIMEOUT, ?MODULE, markAsFinished, [self(), erlang:timestamp()]),
{Board, Status2, Players}.
handle_call({place, Action, Player, Position}, _, State = {Board, Status, _Players}) ->
% TODO: check for is playing
CurrentPlayer = maps:get(current_player, Status),
Result = s:s(Board, {place, CurrentPlayer, {Action, Player, Position}}),
{Response, State2} = processResult(Result, State),
{reply, Response, State2};
handle_call(timeout, TimeoutPid, State = {_Board, _Status, Players}) ->
TimeoutPlayer = getPlayerForClockPid(TimeoutPid, Players),
WinningPlayer = otherPlayer(TimeoutPlayer),
{Res, State2} = winGame(WinningPlayer, State),
{reply, Res, State2};
handle_call(finished, _, {Board, Status, Players}) ->
Status2 = maps:put(result, finished, Status),
State2 = {Board, Status2, Players},
{reply, State2, State2};
handle_call(assign_players, _, {Board, Status}) ->
Players = createPlayers(self()),
State2 = {Board, Status, Players},
{reply, State2, State2};
handle_call(info, _, State = {Board, Status, #{x := X, o := O}}) ->
BoardInfo = s:s(Board, info),
RX = playerInfo(X),
RO = playerInfo(O),
Res = #{board => BoardInfo,
status => Status,
players => #{x => RX, o => RO}},
{reply, Res, State};
handle_call(_, _, State) ->
{reply, State, State}.
playerInfo({Clock, Actions}) ->
{Next, Current} = s:s(Actions, info),
{{Time, _ ,_}, _} = s:s(Clock, info),
#{clock => Time, actions => #{next => Next, current => Current}}.
getPlayerForClockPid(ClockPid, Players) ->
getPlayerForClockPid(ClockPid, Players, maps:keys(Players)).
getPlayerForClockPid(ClockPid, Players, [H | T]) ->
case maps:get(H, Players) of
{ClockPid, _} -> H,
getPlayerForClockPid(ClockPid, Players, T)
end.
actionProcess(x) -> actions:go(1);
actionProcess(o) -> actions:go(2).
playerProcesses(Pid, Player) ->
{ok, Clock} = clock:go(Pid),
{ok, Actions} = actionProcess(Player),
{Clock, Actions}.
playerNames() ->
[x, o].
createPlayers(Self) ->
createPlayers(Self, playerNames(), #{}).
createPlayers(_Self, [], Players) ->
Players;
createPlayers(Self, [H | T], Players) ->
createPlayers(Self, T, maps:put(H, playerProcesses(Self, H), Players)).
defaultStatus() ->
#{current_player => x,
result => playing}.
init([]) ->
{ok, Board} = board:go(),
Status = defaultStatus(),
{ok, {Board, Status}}.
go() ->
{ok, Pid} = gen_server:start_link(?MODULE, [], []),
s:s(Pid, assign_players),
{ok, Pid}.
terminate(_, State = {_Board, Status, Players}) ->
% gen_server:stop(Board),
CurrentPlayer = maps:get(current_player, Status),
OtherPlayer = otherPlayer(CurrentPlayer),
{OClock, OActions} = maps:get(OtherPlayer, Players),
CurrentPlayer = maps:get(current_player, Status),
{CClock, CActions} = maps:get(CurrentPlayer, Players),
gen_server:stop(OClock),
gen_server:stop(CClock),
gen_server:stop(OActions),
gen_server:stop(CActions),
io:format("Table Terminating.~p~n", [State]),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
handle_cast(_, State) ->
{noreply, State}.
handle_info(Msg, State) ->
io:format("Unexpected message: ~p~n",[Msg]),
{noreply, State}.
What I'm confused about is it seems to be crashing from calling the Table (the Host variable) with timeout but I don't see a stacktrace with anything about the Table code.
s:s(Pid, Msg) is a convenience for gen_server:call(Pid, Msg).
How can I debug what's causing the crash?
EDIT:
Changed the timeout atom to clockdone since timeout has special case in Erlang.
Now getting this crash:
=ERROR REPORT==== 5-Sep-2017::11:37:07 ===
** Generic server <0.570.0> terminating
** Last message in was {tick,25.1116}
** When Server state == {{20.927900000000147,ticking,#Ref<0.0.5.642>},
<0.566.0>}
** Reason for termination ==
** {{timeout,{gen_server,call,[<0.566.0>,clockdone]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,204}]},
{clock,updateTick,2,[{file,"src/clock.erl"},{line,27}]},
{clock,handle_call,3,[{file,"src/clock.erl"},{line,35}]},
{gen_server,try_handle_call,4,[{file,"gen_server.erl"},{line,615}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,647}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]}
which if I understand right, means the server <0.566.0> died.

You should start with 6.1 Simple Debugging from OTP Design Principles User's Guide.
If it doesn't help try use dbg module from Runtime_Tools. Look at gen_server:call/2. It seems that exception is generated at https://github.com/erlang/otp/blob/master/lib/stdlib/src/gen_server.erl#L206 which indicates that catch gen:call(Host, '$gen_call', timeout) returns {'EXIT',{timeout,{gen_server,call,[<0.532.0>,timeout]}}} for some reason. You could go deeper and see why it happen. I vaguely remember there was some trick or catch which could cause gen:call/3 could return {'EXIT',Reason} instead of throwing an exception but I don't recall details and don't know it is the cause of your trouble.
You could also try observer if you do like GUI.
Anyway creating Minimal, Complete, and Verifiable example would help a lot to obtain a better answer to your question.

Related

Swift Array returning empty after leaving function

When running the code below, returning coordsArray returns no value when outside of the getCrimes function, but prints a value when inside. I read online about using something called a semaphore but have no experience in that so was hoping someone could guide me on this and how to correctly return coordsArray
func getCrimes(completion: #escaping ([Crime])->()){
//guard let crimeURL = URL(string: "https://data.police.uk/api/crimes-street/drugs?poly=51.5623,-2.5709:51.4952,-2.7292:51.4008,-2.6235:51.4028,-2.4875:51.4569,-2.4274&date=\(currentYear)-\(currentMonth)") else {return}
guard let crimeURL = URL(string: "https://data.police.uk/api/crimes-street/drugs?poly=51.5623,-2.5709:51.4952,-2.7292:51.4008,-2.6235:51.4028,-2.4875:51.4569,-2.4274&date=2022-02") else {return}
URLSession.shared.dataTask(with: crimeURL) { (data, _, _) in
let crimes = try! JSONDecoder().decode([Crime].self, from: data!)
DispatchQueue.main.async{
completion(crimes)
}
}
.resume()
}
func getLocations() -> [Coords] {
var coordsArray : [Coords] = []
getCrimes{ (crimes) in
self.crimes = crimes
for crime in crimes{
let lat = Double(crime.location.latitude)!
let long = Double(crime.location.longitude)!
coordsArray.append(Coords(latitude:lat, longitude: long))
}
print(coordsArray) //Does display the array
}
print(coordsArray)//Doesn't display the array
return coordsArray //Blank Array
}
You execute a async function in main thread. Main thread will not wait for that async thread, it continues to execute next command in function.
func getLocations() -> [Coords] {
// Main Thread
var coordsArray : [Coords] = []
getCrimes { (crimes) in
// Async Thread, suppose 100ms to complete
self.crimes = crimes
for crime in crimes{
let lat = Double(crime.location.latitude)!
let long = Double(crime.location.longitude)!
coordsArray.append(Coords(latitude:lat, longitude: long))
}
print(coordsArray)
}
// Main thread, very fast, execute immediately after execute `getCrimes`,
// => coordsArray = [] because Async Task not complete
print(coordsArray)
return coordsArray
}
You can wait at main thread, but it shouldn't be use because it lock main and UI will stop
func getCrimes(completion: #escaping ([Crime])->()){
//guard let crimeURL = URL(string: "https://data.police.uk/api/crimes-street/drugs?poly=51.5623,-2.5709:51.4952,-2.7292:51.4008,-2.6235:51.4028,-2.4875:51.4569,-2.4274&date=\(currentYear)-\(currentMonth)") else {return}
guard let crimeURL = URL(string: "https://data.police.uk/api/crimes-street/drugs?poly=51.5623,-2.5709:51.4952,-2.7292:51.4008,-2.6235:51.4028,-2.4875:51.4569,-2.4274&date=2022-02") else {return}
URLSession.shared.dataTask(with: crimeURL) { (data, _, _) in
let crimes = try! JSONDecoder().decode([Crime].self, from: data!)
// The main thread was lock, so need execute completion at other thread
DispatchQueue.global(qos: .default).async{
completion(crimes)
}
}
.resume()
}
func getLocations() -> [Coords] {
var coordsArray : [Coords] = []
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
getCrimes{ (crimes) in
self.crimes = crimes
for crime in crimes{
let lat = Double(crime.location.latitude)!
let long = Double(crime.location.longitude)!
coordsArray.append(Coords(latitude:lat, longitude: long))
}
print(coordsArray) //Does display the array
dispatchGroup.leave()
}
dispatchGroup.wait() // Lock thread until complete async task
print(coordsArray)//Doesn't display the array
return coordsArray //Blank Array
}
Instead of, you can use escaping closure
func getLocations(completeGetLocation: (([Coords]) -> Void)?) {
var coordsArray : [Coords] = []
getCrimes { (crimes) in
self.crimes = crimes
for crime in crimes{
let lat = Double(crime.location.latitude)!
let long = Double(crime.location.longitude)!
coordsArray.append(Coords(latitude:lat, longitude: long))
}
print(coordsArray) //Does display the array
completeGetLocation?(coordsArray)
}
}

Fable.React - editing an Input field moves cursor to end of text

I'm working with the Elmish.Bridge flavor of the SAFE stack.
At the top level of the view, I create an input field like this:
Input.input [ Input.Value(model.FieldValue); Input.OnChange(fun e -> dispatch (EditFieldValue(e.Value))) ]
When I edit that field's value by typing in the middle of it, the model is updated as expected, but the cursor also moves to the end of the input's text.
My model is several layers deep and is completely made of serializable types (primitives, strings, collections, records, and unions).
I tried reproducing this in a toy app (where the model is much less complicated), but it works as expected there - the cursor maintains position.
Is there any way to determine why the cursor is moving under these circumstances?
Using the DefaultValue instead of Value should fix this.
This works with Fable.React and Fable.Elmish. Using DefaultValue will not initiate an update of the component / DOM in the react lifecyle and you should therefore not get the cursor changes.
From the link below:
react uncontrolled components
In the React rendering lifecycle, the value attribute on form elements will override the value in the DOM. With an uncontrolled component, you often want React to specify the initial value, but leave subsequent updates uncontrolled. To handle this case, you can specify a defaultValue attribute instead of value.
I had a similar issue, and it turned out to be because React was deleting my DOM object and recreating it. (That in turn was caused by the fact that my React element, though identical in both code branches, had different elements as parents in the two different branches.)
If React does not delete/recreate your DOM object, your cursor should behave normally. If you want to figure out what is causing your DOM to be updated/created, adding some printfns inside of a Hooks.useEffectDisposable call. Add one printfn inside the effect, which will tell you when the element is being mounted, and another inside of the IDisposable to tell you when it's being unmounted.
Using an uncontrolled component can work in certain circumstances, but it will prevent your text from updating if something else changes in the model, e.g. after someone hits an "OK" button you can't clear the textbox. It's better to just get your head around the React lifecycle and fix whatever issue is causing React to re-render your DOM.
at first, my english is not good. please understand it.
i experienced the problem too, so i tried to find a workaround.
i just guess that the reason may be time interval between dom event and react.js's re-rendering, but i cant make sure that.
a key point about the workaround is to set/get cursor position manually in componentDidUpdate lifecycle.
below are example code.
i hope that it would be helpful.
module NotWorks =
type MsgX = | InputX of string
| InputX2 of string
let subjectX , observableX = FSharp.Control.AsyncRx.subject<MsgX>()
type ModelX = {
input : string
input2 : string
}
let updateX (modelX : ModelX) (msgX : MsgX) : ModelX =
match msgX with
| MsgX.InputX(s) -> { modelX with input = (s) }
| MsgX.InputX2(s) -> { modelX with input2 = (s) }
type ComponentX(modelX : ModelX) as this =
inherit Fable.React.Component<ModelX, unit>(modelX) with
override _.render() : ReactElement =
div []
[
textarea
[
OnChange this.OnChange
Value this.props.input
Rows 10
Cols 40
]
[]
textarea
[
OnChange this.OnChange2
Value this.props.input2
Rows 10
Cols 40
]
[]
]
override _.componentDidUpdate(_ : ModelX, _ : unit) : unit =
()
member _.OnChange(e : Browser.Types.Event) : unit =
let target = e.target :?> Browser.Types.HTMLTextAreaElement
async {
// in here, no interval. but the problem can appeared sometimes.
do! subjectX.OnNextAsync(MsgX.InputX(target.value))
} |> Async.Start
member _.OnChange2(e : Browser.Types.Event) : unit =
let target = e.target :?> Browser.Types.HTMLTextAreaElement
let x = target.value
async {
do! Async.Sleep(1) // this makes interval. the problem appears always.
do! subjectX.OnNextAsync(MsgX.InputX2(x))
} |> Async.Start
let viewX (modelX : ModelX) (_ : Dispatch<MsgX>) : ReactElement =
Fable.React.Helpers.ofType<ComponentX, ModelX, unit>(modelX) []
let componentX =
Fable.Reaction.Reaction.StreamView
{input = ""; input2 = ""}
viewX
updateX
(fun _ o ->
o
|> FSharp.Control.AsyncRx.merge observableX
|> Fable.Reaction.AsyncRx.tag "x"
)
module Works =
type MsgX = | InputX of string * int * int
let subjectX , observableX = FSharp.Control.AsyncRx.subject<MsgX>()
type ModelX = {
input : string * int * int
}
let updateX (modelX : ModelX) (msgX : MsgX) : ModelX =
match msgX with
| MsgX.InputX(s, start, ``end``) -> { modelX with input = (s, start, ``end``) }
type ComponentX(modelX : ModelX) as this =
inherit Fable.React.Component<ModelX, unit>(modelX) with
// we need a ref to get/set cursor position.
let mutable refTextArea : Option<Browser.Types.HTMLTextAreaElement> = None
override _.render() : ReactElement =
let s, _, _ = this.props.input
textarea
[
Props.Ref(fun e -> refTextArea <- Some(e :?> Browser.Types.HTMLTextAreaElement))
OnChange this.OnChange
Value s
Rows 10
Cols 40
]
[]
override _.componentDidUpdate(_ : ModelX, _ : unit) : unit =
// set cursor position manually using saved model data.
refTextArea
|> Option.iter (fun elem ->
let _, start, ``end`` = this.props.input // must use current model data but not previous model data.
elem.selectionStart <- start;
elem.selectionEnd <- ``end``
)
member _.OnChange(e : Browser.Types.Event) : unit =
let target = e.target :?> Browser.Types.HTMLTextAreaElement
// save cursor position.
let x = target.value
let start = target.selectionStart
let ``end``= target.selectionEnd
async {
do! subjectX.OnNextAsync(MsgX.InputX(x, start, ``end``))
} |> Async.Start
let viewX (modelX : ModelX) (_ : Dispatch<MsgX>) : ReactElement =
Fable.React.Helpers.ofType<ComponentX, ModelX, unit>(modelX) []
let componentX =
Fable.Reaction.Reaction.StreamView
{input = "", 0, 0}
viewX
updateX
(fun _ o ->
o
|> FSharp.Control.AsyncRx.merge observableX
|> Fable.Reaction.AsyncRx.tag "x"
)
i missed something that i had to say.
if there are one more textarea tags, above workaround causes focus changing always.
so there should be some logic to prevent this.
module Works =
type MsgX = | InputX of string * int * int
| InputX2 of string
| ChangeTime
let subjectX , observableX = FSharp.Control.AsyncRx.subject<MsgX>()
type ModelX = {
// use timestamp to determine whether there should be setting cursor position or not.
time : int
input : int * string * int * int
input2 : string
}
let updateX (modelX : ModelX) (msgX : MsgX) : ModelX =
match msgX with
| MsgX.InputX(s, start, ``end``) ->
{ modelX with
time = modelX.time + 1;
input = (modelX.time + 1, s, start, ``end``)
}
| ChangeTime -> { modelX with time = modelX.time + 1 }
| MsgX.InputX2 s -> { modelX with input2 = s }
type ComponentX(modelX : ModelX) as this =
inherit Fable.React.Component<ModelX, unit>(modelX) with
let mutable refTextArea : Option<Browser.Types.HTMLTextAreaElement> = None
override _.render() : ReactElement =
let _, s, _, _ = this.props.input
div []
[
textarea
[
Props.Ref(fun e -> refTextArea <- Some(e :?> Browser.Types.HTMLTextAreaElement))
OnChange this.OnChange
OnBlur this.OnBlur
Value s
Rows 10
Cols 40
]
[]
textarea
[
OnChange this.OnChange2
Value this.props.input2
]
[]
]
override _.componentDidUpdate(_ : ModelX, _ : unit) : unit =
refTextArea
|> Option.filter (fun _ ->
// determine whether there should be setting cursor position or not.
let time, _, _, _ = this.props.input
time = this.props.time
)
|> Option.iter (fun elem ->
let _, _, start, ``end`` = this.props.input
elem.selectionStart <- start;
elem.selectionEnd <- ``end``
)
member _.OnChange(e : Browser.Types.Event) : unit =
let target = e.target :?> Browser.Types.HTMLTextAreaElement
let x = target.value
let start = target.selectionStart
let ``end``= target.selectionEnd
async {
do! subjectX.OnNextAsync(MsgX.InputX(x, start, ``end``))
} |> Async.Start
member _.OnChange2(e : Browser.Types.Event) : unit =
subjectX.OnNextAsync(MsgX.InputX2 e.Value) |> Async.Start
member _.OnBlur(e : Browser.Types.Event) : unit =
subjectX.OnNextAsync(MsgX.ChangeTime) |> Async.Start
let viewX (modelX : ModelX) (_ : Dispatch<MsgX>) : ReactElement =
Fable.React.Helpers.ofType<ComponentX, ModelX, unit>(modelX) []
let componentX =
Fable.Reaction.Reaction.StreamView
{time = 1; input = 0, "", 0, 0; input2 = ""}
viewX
updateX
(fun _ o ->
o
|> FSharp.Control.AsyncRx.merge observableX
|> Fable.Reaction.AsyncRx.tag "x"
)
mountById "elmish-app" (ofFunction componentX () [])
the key point is to use timestamp.
of course, i admit that this workaround is quite complex and ugly.
just reference it please.
Use Fable.React.Helpers.valueOrDefault instead of DefaultValue or Value:
/// `Ref` callback that sets the value of an input textbox after DOM element is created.
// Can be used instead of `DefaultValue` and `Value` props to override input box value.
let inline valueOrDefault value =
Ref <| (fun e -> if e |> isNull |> not && !!e?value <> !!value then e?value <- !!value)
Maximillian points out in his answer that it's because React is recreating the DOM object. In my attempt to figure out how to use hooks to determine why, I ended up learning about React Hooks and about how to use them from Fable, but I never figured out how to use them to figure out what's causing the DOM object to be deleted.
A solution to keep this from happening is to augment your onChange function as follows.
let onChangeWithCursorAdjustment (myOnChangeFunction: Browser.Types.Event -> unit) (event: Browser.Types.Event) =
let target = event.target :?> Browser.Types.HTMLInputElement
let prevBeginPos = target.selectionStart
let prevEndPos = target.selectionEnd
myOnChangeFunction event
Browser.Dom.window.requestAnimationFrame (fun _ ->
target.selectionStart <- prevBeginPos
target.selectionEnd <- prevEndPos
) |> ignore
And use it like this (for an text input field):
input [] [
Type "text"
Value someChangingValue
OnChange (onChangeWithCursorAdjustment myOnChangeFunction)
]
This will stop the cursor jumping to the end of the input field.

How to query multiple keys with Firebase GeoFire using Swift?

I've got a GeoFire query that is bringing back user id's or keys. I'm getting back keys in sequence, but I'm getting several sequences. How can I get the last updated sequence?
#IBAction func friendsNearMeACTN(sender: AnyObject)
{
let geofireRef = self.ref.child("UserLocations")
let geoFire = GeoFire(firebaseRef: geofireRef)
let circleQuery = geoFire.queryAtLocation(self.location, withRadius: 20.6)
circleQuery.observeEventType(.KeyEntered, withBlock: { (key: String!, location: CLLocation!) in
self.localUsers.append(key)
self.getLocalUsers()
})
}
func getLocalUsers()
{
print(self.localUsers)
}
This is what I'm getting back from func getLocalUsers()....
["WGueYzDjH4NW2vneHOyGmjf6PYB3"]
["WGueYzDjH4NW2vneHOyGmjf6PYB3", "Cg4pQj36ttNUuWNqtc16tIFmI0A2"]
["WGueYzDjH4NW2vneHOyGmjf6PYB3", "Cg4pQj36ttNUuWNqtc16tIFmI0A2", "N5pgqGEhW2f7PGGVmB3AQ8v1uPk2"]
How can I simply get the last array?
The answer is that the GeoFire query is a continuous asynchronous call and needs the final code observeReadyWithBlock to feed the self.localUsers() the collected information only. Here is the example...
let regionQuery = geoFire.queryWithRegion(self.region)
regionQuery.observeEventType(.KeyEntered, withBlock: { (key: String!, location: CLLocation!) in
var users = [String]()
users.append(key)
for keys in users
{
let user = keys
allKeys.append(user)
}
self.localUsers = allKeys
self.getLocalUsers()
})
regionQuery.observeReadyWithBlock({ () -> Void in
self.getLocalUsers()
})
The problem here is that you are calling getLocalUsers func every time the observer block is fired. You are calling it for every single result. You need to count the results and add 1 to the count every time the observer block is executed. When your count reaches the result count, call the getLocalUsers function once instead of 3 times. Try the code below. I have not tested it.
#IBAction func friendsNearMeACTN(sender: AnyObject){
var i = 0//The counter
let geofireRef = self.ref.child("UserLocations")
let geoFire = GeoFire(firebaseRef: geofireRef)
let circleQuery = geoFire.queryAtLocation(self.location, withRadius: 20.6)
circleQuery.observeEventType(.KeyEntered, withBlock: { (key: String!, location: CLLocation!) in
self.localUsers.append(key)
i += 1//Add one to i every time observer fires
if i == self.key.count {//if counter (i) is equal to the keys returned call getLocalUsers func once
self.getLocalUsers()
}
})
}
func getLocalUsers(){
print(self.localUsers)
}

Convert HList to Map with shapless

i start work with shapeless, and as proof of concept, i defined function for convert Hlist to Map, but something went wrong, can you help me please?
object bar extends Poly1 {
implicit def caseTuple[T, U](implicit st: Case.Aux[T, Map[String, Any]], su: Case.Aux[U, Map[String, Any]]) =
at[(T, U)](t => {
Map(s"${t._1}" -> t._2)
})
}
object foo extends Poly2 {
implicit def default[T](implicit st: bar.Case.Aux[T, Map[String, Any]]) =
at[Map[String, Any], T] { (acc, t) =>
acc ++ bar(t)
}
}
val h = ("k1", 1) :: ("k2", "foo") :: HNil
println(h.foldLeft(Map.empty[String, Any])(foo))
But i got error: could not find implicit value for parameter folder: shapeless.ops.hlist.LeftFolder[shapeless.::[(String, Int),shapeless.::[(String, String),shapeless.HNil]],scala.collection.immutable.Map[String,Any],Main.foo.type]
[error] println(h.foldLeft(Map.empty[String, Any])(foo))
As you don't care about the value type, you don't need dedicated polymorphic function for the value type.
If you're just relying on .toString, you don't need a Poly1 so that the above example can be rewritten like this:
object foo1 extends Poly2 {
implicit def default[K, V] =
at[Map[String, Any], (K, V)] { case (acc, (k, v)) =>
acc + (k.toString -> v)
}
}
val h = ("k1", 1) :: ("k2", "foo") :: (true, "TRUE") :: HNil
println(h.foldLeft(Map.empty[String, Any])(foo1))
// Map(k1 -> 1, k2 -> foo, true -> TRUE)
If you have multiple types in the key position, you need to convert them to string. It can be Poly1 or any typeclass such as scalaz.Show or cats.Show which provide A => String.
object bar2 extends Poly1 {
implicit val stringCase = at[String](identity)
implicit val symbolCase = at[Symbol](_.name)
implicit def integralCase[A: Integral] = at[A](_.toString)
implicit def fractionalCase[A: Fractional] = at[A](_ formatted "%.2f")
}
object foo2 extends Poly2 {
implicit def default[K, V](implicit bk: bar2.Case.Aux[K, String]) =
at[Map[String, Any], (K, V)] { case (acc, (k, v)) =>
acc + (bk(k) -> v)
}
}
val h2 = ("k1", 1) :: ("k2", "foo") :: ('k3, true) :: (12.3456, 'frac) :: HNil
println(h2.foldLeft(Map.empty[String, Any])(foo2))
// Map(k1 -> 1, k2 -> foo, k3 -> true, 12.35 -> 'frac)
You can do the same to have another key type instead of Any.

Using untyped F# quotations to copy an array without knowing the type

I'm working on a small project to using quotations to clone trees of some basic record types and I have it working in most cases, the big problem I'm having is with arrays.
module FSharpType =
/// predicate for testing types to see if they are generic option types
let IsOption (stype: System.Type) = stype.Name = "FSharpOption`1"
/// predicate for testing types to see if they are generic F# lists
let IsList (stype: System.Type) = stype.Name = "FSharpList`1"
module RecordCloning =
let inline application prms expr = Expr.Application(expr, prms)
let inline coerse typ expr = Expr.Coerce(expr, typ)
let (|IsMapType|_|) (t: Type) =
if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<Map<_,_>> then Some t
else None
let rec copyThing (mtype: Type) : Expr =
match mtype with
| _ when FSharpType.IsRecord mtype -> genRecordCopier mtype
| _ when FSharpType.IsUnion mtype -> genUnionCopier mtype
| _ when mtype.IsValueType || mtype = typeof<String> -> <## id ##>
| _ when mtype.IsArray -> genArrayCopier mtype
| IsMapType t -> <## id ##>
| _ when mtype = typeof<System.Object> -> <## id ##>
| _ -> failwithf "Unexpected Type: %s" (mtype.ToString())
and genRecordCopier (rtype: Type) : Expr =
let arg = Var("x", typeof<obj>, false)
let argExpr = Expr.Var(arg)
let useArg = Expr.Coerce(argExpr, rtype)
let fields = FSharpType.GetRecordFields(rtype)
let members = [ for field in fields -> genFieldCopy useArg field ]
let newrec = Expr.Coerce(Expr.NewRecord(rtype, members),typeof<obj>)
Expr.Lambda(arg, newrec)
and genFieldCopy argExpr (field: PropertyInfo) : Expr =
let pval = Expr.PropertyGet(argExpr, field)
let convfun = copyThing field.PropertyType
let applied = Expr.Application (convfun, Expr.Coerce(pval, typeof<obj>))
Expr.Coerce(applied, field.PropertyType)
and castToType (atype : Type) : Expr =
let arg = Var("x", typeof<obj>, false)
let argExpr = Expr.Var(arg)
Expr.Lambda(arg, Expr.Coerce(argExpr, atype))
and coerseLambda (outterType: Type) (lambda: Expr) : Expr =
let arg = Var("x", outterType, false)
let argExpr = Expr.Var(arg)
let wrappedLambda =
lambda
|> application (argExpr |> coerse typeof<obj>)
|> coerse outterType
Expr.Lambda(arg, wrappedLambda)
and genArrayCopier (atype : Type) : Expr =
let etype = atype.GetElementType()
let copyfun = copyThing etype
let arg = Var("arr", typeof<obj>, false)
let argExpr = Expr.Var(arg) |> coerse atype
let wrappedLambda = coerseLambda etype copyfun
let func = <## Array.map (%%wrappedLambda) (%%argExpr) ##>
Expr.Lambda(arg, func)
and genOptionCopier (otype: Type) : Expr =
let etype = otype.GetGenericArguments().[0]
let copyfun = copyThing etype
<## fun (inobj: obj) ->
let x = inobj :?> Option<'t>
match x with
| Some v -> Some <| (%%copyfun) (box v)
| None -> None
|> box
##>
and genUnionCopier (utype: Type) : Expr =
let cases = FSharpType.GetUnionCases utype
// if - union case - then - copy each field into new case - else - next case
let arg = Var("x", typeof<obj>, false)
let argExpr = Expr.Var(arg)
let useArg = Expr.Coerce(argExpr, utype)
let genCaseTest case = Expr.UnionCaseTest (useArg, case)
let makeCopyCtor (ci: UnionCaseInfo) =
let copiedMembers = [ for field in ci.GetFields() -> genFieldCopy useArg field ]
Expr.Coerce(Expr.NewUnionCase(ci, copiedMembers), typeof<obj>)
let genIf ifCase thenCase elseCase = Expr.IfThenElse(ifCase, thenCase, elseCase)
let nestedIfs =
cases
|> Array.map (fun case -> genIf (genCaseTest case) (makeCopyCtor case))
|> Array.foldBack (fun iff st -> iff st) <| <## failwith "Unexpected Case Condition" ##>
let newunion = Expr.Coerce(nestedIfs,typeof<obj>)
Expr.Lambda(arg, newunion)
let wrapInType<'I,'O> (lambdaExpr: Expr) : Expr<'I -> 'O> =
<# fun (v : 'I) -> (%%lambdaExpr : obj -> obj) (box v) :?> 'O #>
let toLinq<'I,'O> (expr: Expr<'I -> 'O>) =
let linq = Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToExpression expr
let call = linq :?> MethodCallExpression
let lambda = call.Arguments.[0] :?> LambdaExpression
Expression.Lambda<Func<'I,'O>>(lambda.Body, lambda.Parameters)
let genrateRecordDeepCopyFunction<'T> () : ('T -> 'T) =
let expr = genRecordCopier typeof<'T>
let castExpr : Expr<obj -> obj> = expr |> Expr.Cast
let compiledExpr = (castExpr |> toLinq).Compile()
fun (v : 'T) -> compiledExpr.Invoke(box v) :?> 'T
I've tried several approaches, but I always get complaints about wanting (string -> string) but getting (obj -> obj) or wanting (object [] -> object []) but getting (string [] -> string []). Any ideas?
Here's a simple test case.
type SimpleArrayRecord = { Names: string array }
[<Fact>]
let ``record cloning should be able to clone a record with a simple array`` () =
let sr = { Names = [|"Rick"; "David"; "Mark"; "Paul"; "Pete"|] }
let func = RecordCloning.genrateRecordDeepCopyFunction<SimpleArrayRecord>()
let res = func sr
Assert.Equal(sr, res)
Here's the method that has gotten me the furthest. The issue seems to be that I can't get it to make the array typed, and so it always fails on the cast when trying to build the record. Adding a cast in the comprehension doesn't help.
and genArrayCopier (atype : Type) : Expr =
let etype = atype.GetElementType()
let copyfun = copyThing etype
let arg = Var("arr", typeof<obj>, false)
let argExpr = Expr.Var(arg) |> coerse atype
<## fun (inobj: obj) ->
let arr = inobj :?> obj[] in
[| for i = 0 to arr.Length - 1 do yield (%%copyfun) (Array.get arr i) |] |> box ##>
Toyvo's solution below works for the example above but not for arrays of records:
type SimpleRecord = { Name: string; Age: int }
type LotsOfRecords = { People: SimpleRecord [] }
[<Fact>]
let ``record cloning should be able to clone a record with an array of records`` () =
let sr = { People = [|{Name = "Rick"; Age = 33 }; { Name = "Paul"; Age = 55 }|] }
let func = RecordCloning.genrateRecordDeepCopyFunction<LotsOfRecords>()
let res = func sr
Assert.Equal(sr, res)
For those who come later, here's the working code. I removed Option and haven't taken the time to clean it up but it's otherwise pretty decent.
let inline application prms expr = Expr.Application(expr, prms)
let inline coerse typ expr = Expr.Coerce(expr, typ)
let inline newrec typ args = Expr.NewRecord(typ, args)
let (|IsMapType|_|) (t: Type) =
if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<Map<_,_>> then Some t
else None
let rec copyThing (mtype: Type) : Expr =
match mtype with
| _ when FSharpType.IsRecord mtype -> genRecordCopier mtype
| _ when FSharpType.IsUnion mtype -> genUnionCopier mtype
| _ when mtype.IsValueType || mtype = typeof<String> -> getIdFunc mtype
| _ when mtype.IsArray -> genArrayCopier mtype
| IsMapType t -> getIdFunc mtype
| _ when mtype = typeof<System.Object> -> getIdFunc mtype
| _ -> failwithf "Unexpected Type: %s" (mtype.ToString())
and X<'T> : 'T = Unchecked.defaultof<'T>
and getMethod =
function
| Patterns.Call (_, m, _) when m.IsGenericMethod -> m.GetGenericMethodDefinition()
| Patterns.Call (_, m, _) -> m
| _ -> failwith "Incorrect getMethod Pattern"
and getIdFunc itype =
let arg = Var("x", itype, false)
let argExpr = Expr.Var(arg)
let func =
let m = (getMethod <# id X #>).MakeGenericMethod([|itype|])
Expr.Call(m, [argExpr])
Expr.Lambda(arg, func)
and genRecordCopier (rtype: Type) : Expr =
let arg = Var("x", rtype, false)
let argExpr = Expr.Var(arg) //|> coerse rtype
let newrec =
FSharpType.GetRecordFields(rtype) |> Array.toList
|> List.map (fun field -> genFieldCopy argExpr field)
|> newrec rtype
Expr.Lambda(arg, newrec)
and genFieldCopy argExpr (field: PropertyInfo) : Expr =
let pval = Expr.PropertyGet(argExpr, field)
copyThing field.PropertyType |> application pval
and genArrayCopier (atype : Type) : Expr =
let etype = atype.GetElementType()
let copyfun = copyThing etype
let arg = Var("arr", atype, false)
let argExpr = Expr.Var(arg)
let func =
let m = (getMethod <# Array.map X X #>).MakeGenericMethod([|etype; etype|])
Expr.Call(m, [copyfun; argExpr])
Expr.Lambda(arg, func)
and genUnionCopier (utype: Type) : Expr =
let cases = FSharpType.GetUnionCases utype
// if - union case - then - copy each field into new case - else - next case
let arg = Var("x", utype, false)
let useArg = Expr.Var(arg)
let genCaseTest case = Expr.UnionCaseTest (useArg, case)
let makeCopyCtor (ci: UnionCaseInfo) =
let copiedMembers = [ for field in ci.GetFields() -> genFieldCopy useArg field ]
Expr.NewUnionCase(ci, copiedMembers)
let genIf ifCase thenCase elseCase = Expr.IfThenElse(ifCase, thenCase, elseCase)
let typedFail (str: string) =
let m = (getMethod <# failwith str #>).MakeGenericMethod([|utype|])
Expr.Call(m, [ <# str #> ])
let nestedIfs =
cases
|> Array.map (fun case -> genIf (genCaseTest case) (makeCopyCtor case))
|> Array.foldBack (fun iff st -> iff st) <| (typedFail "Unexpected Case in Union")
Expr.Lambda(arg, nestedIfs)
Now it actually works with unions as well. Cheers!
If you do this, make sure you understand generics and how to generate them. You are in LISP land, type system won't help you, since it cannot reason about itself - you are manipulating F# terms with F#.
and getMethod q =
match q with
| Patterns.Call (_, m, _) ->
if m.IsGenericMethod then
m.GetGenericMethodDefinition()
else
m
| _ -> failwith "getMethod"
and X<'T> : 'T =
Unchecked.defaultof<'T>
and genArrayCopier (atype : Type) : Expr =
let etype = atype.GetElementType()
let copyfun = copyThing etype
let arg = Var("arr", typeof<obj>, false)
let argExpr = Expr.Var(arg) |> coerse atype
let wrappedLambda = coerseLambda etype copyfun
let func =
let m = getMethod <# Array.map X X #> // obtained (forall 'X 'Y, 'X[] -> 'Y[])
let m = m.MakeGenericMethod([| etype; etype |]) // specialized to 'E[] -> 'E[]
Expr.Call(m, [wrappedLambda; argExpr]) // now this type-checks
Expr.Lambda(arg, func)

Resources