Why does the following code cause the Erlang HiPE compiler crash? - erlang

The code is following:
-module(hipe_crash).
-export([f/6]).
f(A, B, C, D, E, L) ->
lists:foldl(fun (X, P) ->
AVar = case A of
0 -> 1 / D;
N -> N / (C - B)
end,
BVar = case E of
atom1 -> 1.0;
atom2 -> 0.8;
_ -> E
end,
CVar = case X of
atom1 -> 0.1 * AVar;
_ -> 1.0
end,
P * BVar * CVar
end, 1, L).
Compiling this code with erlc no error occur,compiling is ok.
But when I compile it with erlc +native ,the compiler crashed,with information:
<HiPE (v 3.9.2)> EXITED with reason {function_clause,[{hipe_icode_fp,assert_assigned,[[{30,{icode_variable,40,fvar,[]}}]],[{file,[104,105,112,101,95,105,99,111,100,101,95,102,112,46,101,114,108]},{line,772}]},{hipe_icode_fp,bindings_are_assigned,1,[{file,[104,105,112,101,95,105,99,111,100,101,95,102,112,46,101,114,108]},{line,766}]},{hipe_icode_fp,filter_map,3,[{file,[104,105,112,101,95,105,99,111,100,101,95,102,112,46,101,114,108]},{line,753}]},{hipe_icode_fp,transform_block,2,[{file,[104,105,112,101,95,105,99,111,100,101,95,102,112,46,101,114,108]},{line,162}]},{hipe_icode_fp,cfg,1,[{file,[104,105,112,101,95,105,99,111,100,101,95,102,112,46,101,114,108]},{line,48}]},{hipe_main,icode_ssa_type,4,[{file,[104,105,112,101,95,109,97,105,110,46,101,114,108]},{line,273}]},{hipe_main,icode_ssa,4,[{file,[104,105,112,101,95,109,97,105,110,46,101,114,108]},{line,255}]},{hipe_main,compile_icode,5,[{file,[104,105,112,101,95,109,97,105,110,46,101,114,108]},{line,109}]}]} #hipe:829
hipe_crash.erl:none: internal error in native_compile;
crash reason: {{hipe,829,
{function_clause,
[{hipe_icode_fp,assert_assigned,
[[{30,{icode_variable,40,fvar,[]}}]],
[{file,"hipe_icode_fp.erl"},{line,772}]},
{hipe_icode_fp,bindings_are_assigned,1,
[{file,"hipe_icode_fp.erl"},{line,766}]},
{hipe_icode_fp,filter_map,3,
[{file,"hipe_icode_fp.erl"},{line,753}]},
{hipe_icode_fp,transform_block,2,
[{file,"hipe_icode_fp.erl"},{line,162}]},
{hipe_icode_fp,cfg,1,
[{file,"hipe_icode_fp.erl"},{line,48}]},
{hipe_main,icode_ssa_type,4,
[{file,"hipe_main.erl"},{line,273}]},
{hipe_main,icode_ssa,4,
[{file,"hipe_main.erl"},{line,255}]},
{hipe_main,compile_icode,5,
[{file,"hipe_main.erl"},{line,109}]}]}},
[{hipe,finalize_fun_sequential,3,
[{file,"hipe.erl"},{line,829}]},
{hipe,'-finalize_fun_concurrent/3-fun-3-',4,
[{file,"hipe.erl"},{line,795}]}]}
I found this code in :http://erlang.2086793.n4.nabble.com/internal-error-in-native-compile-td2298937.html.But I can't get more information about why this code crashes the compiler.

One of the BEAM float-point operations' optimisations is to group float-point operations
into blocks,if some intermediate operation failed,the BEAM will signal badarith. It relies on headware.
I think it's a bug of the exception handler. You can use the no_inline_fp HiPE compiler option at
the expense of disabling all float-point optimisations.

Related

Erlang: Why Dialyzer does not notice this error?

Now, I try to use Dialyzer and use -spec, -type.
I give the below code to Dialyzer, and I expected Dialyzer to notice "hoge(a) + 1 is invalid", but Dialyzer does not notice.
-spec hoge (X) -> bad when X :: a;
(X) -> number() when X :: number().
hoge(X) when is_number(X) -> 1;
hoge(a) -> bad.
foo() ->
_ = hoge(a) + 1.
But, in another setting,
-spec hoge (X) -> bad when X :: a;
(X) -> string() when X :: number().
hoge(X) when is_number(X) -> "1";
hoge(a) -> bad.
foo() ->
_ = hoge(a) + 1.
Dialyzer tell me this error,
test.erl:12: The call erlang:'+'('bad' | [49,...],1) will never return since it differs in the 1st argument from the success typing arguments: (number(),number())
Why Dialyzer can't notice type-error in first setting.
-spec hoge (X) -> bad when X :: a;
(X) -> number() when X :: number().
This contract(specification) means not "hoge is typed of 'a' -> 'bad' | number() -> number()" but "'a' | number() -> 'bad' | number()"?
Here is a complete module for the first example.
-module(example).
-export([hoge/1, foo/0]).
-spec hoge (X) -> bad when X :: a;
(X) -> number() when X :: number().
hoge(X) when is_number(X) -> 1;
hoge(a) -> bad.
foo() ->
_ = hoge(a) + 1.
The standard answer to "why Dialyzer doesn't catch this error" questions is always "because it is never wrong". Dialyzer never promises to find all errors.
In your problematic example, without the spec, Dialyzer's type inference algorithm indeed produces a union type for all the arguments and all the return values. With the spec, Dialyzer still infers the unions, but should have used the spec to narrow down the return value of the call and then produce an error. This looks like a case of "reduced sensitivity" (but not a bug, per se). You could, in any case, file a bug report.
In your working example, any possible value results in a bad result and Dialyzer's own type inference is enough, even without a spec.

Erlang exception error: no true branch found when evaluating an if expression

I don't understand erlang very well and for the past number of hours I've been searching up ways to fix this but I can't find any good If statement examples.
I'm trying to use a recursive function to move discs from one hanoi tower to another. However I get the "no true branch found when evaluating an if expression". I understand as far as needing true -> somewhere?
-module(hanoi).
-export([main/1]).
%% Create Towers
main(NumDiscs) ->
TowerA = create_tower(NumDiscs),
TowerB = [],
TowerC = [],
display_towers(TowerA,TowerB,TowerC),
solve(NumDiscs,TowerA,TowerB,TowerC).
%% Add the number of discs inputted to the first tower
create_tower(0) -> [];
create_tower(NumDiscs) when NumDiscs > 0 ->
[NumDiscs] ++ create_tower(NumDiscs - 1).
%% Display all towers
display_towers(A,B,C) ->
io:format("-------------------------\n"),
io:format("Tower A: ~p\n",[A]),
io:format("Tower B: ~p\n",[B]),
io:format("Tower C: ~p\n",[C]).
solve(Disc,TowA,TowB,TowC) ->
if Disc > 0 ->
solve(Disc - 1,TowA,TowB,TowC),
TowBNew = [lists:last(TowA)],
TowCNew = [lists:append(TowC,TowBNew)],
TowANew = [lists:sublist(TowA,length(TowA) - 1)],
display_towers(TowANew,TowB,TowCNew),
solve(Disc - 1,TowANew,TowCNew,TowB)
end.
An if expression in Erlang must have a branch that evaluates to true. I'm guessing that error is thrown when Disc > 0 is false. You need to add a true -> clause to that:
solve(Disc,TowA,TowB,TowC) ->
if Disc > 0 ->
solve(Disc - 1,TowA,TowB,TowC),
TowBNew = [lists:last(TowA)],
TowCNew = [lists:append(TowC,TowBNew)],
TowANew = [lists:sublist(TowA,length(TowA) - 1)],
display_towers(TowANew,TowB,TowCNew),
solve(Disc - 1,TowANew,TowCNew,TowB); % <- note the ';'
true ->
% do something here
end.
Here is my code from a long time ago. Hope this can help you:
main(N)->
towerhanoi(N, a, b, c).
towerhanoi(N, A, C, B)->
case N of
1 ->
change(N, A, C);
_ ->
towerhanoi(N-1, A, B, C),
change(N, A, C),
towerhanoi(N-1 , B, C, A)
end.
change(N, A, C)->
io:format("change disc ~p from tower ~p to tower ~p~n", [N, A, C]).

F# Create Factorial function without recursion, library functions or loops

In this video about functional programming at 35:14 Jim Weirich writes a function to compute factorial without using recursion, library functions or loops:
see image of Ruby code here
The code in Ruby
fx = ->(improver) {
improver.(improver)
}.(
->(improver) {
->(n) { n.zero ? 1 : n * improver.(improver).(n-1) }
}
)
I'm trying to express this approach F#
let fx =
(fun improver -> improver(improver))(
fun improver ->
fun n ->
if n = 0 then 1
else n * improver(improver(n - 1)))
I'm currently stuck at
Type mismatch. Expecting a 'a but given a 'a -> 'b
The resulting type would be infinite when unifying ''a' and ''a -> 'b'
I can't seem find the right type annotation or other way of expressing the function
Edit:
*without the rec keyword
Languages with ML-style type inference won't be able to infer a type for the term fun improver -> improver improver; they start by assuming the type 'a -> 'b for a lambda-definition (for some undetermined types 'a and 'b), so as the argument improver has type 'a, but then it's applied to itself to give the result (of type 'b), so improver must simultaneously have type 'a -> 'b. But in the F# type system there's no way to unify these types (and in the simply-typed lambda calculus there's no way to give this term a type at all). My answer to the question that you linked to in your comment covers some workarounds. #desco has given one of those already. Another is:
let fx = (fun (improver:obj->_) -> improver improver)
(fun improver n ->
if n = 0 then 1
else n * (improver :?> _) improver (n-1))
This is cheating, but you can use types
type Self<'T> = delegate of Self<'T> -> 'T
let fx1 = (fun (x: Self<_>) -> x.Invoke(x))(Self(fun x -> fun n -> if n = 0 then 1 else x.Invoke(x)(n - 1) * n))
type Rec<'T> = Rec of (Rec<'T> -> 'T)
let fx2 = (fun (Rec(f ) as r) -> f r)(Rec(fun ((Rec f) as r) -> fun n -> if n = 0 then 1 else f(r)(n - 1) * n))

Erlang case statement

I have the following Erlang code and it is giving the warning as follows, when i try to compile it, but that make sense. function need two arguments, but i need to patten match "everything else" rather x, y or z.
-module(crop).
-export([fall_velocity/2]).
fall_velocity(P, D) when D >= 0 ->
case P of
x -> math:sqrt(2 * 9.8 * D);
y -> math:sqrt(2 * 1.6 * D);
z -> math:sqrt(2 * 3.71 * D);
(_)-> io:format("no match:~p~n")
end.
crop.erl:9: Warning: wrong number of arguments in format call.
I was trying an anonymous variable after io:format, but still it is not happy.
In the format you use ~p. It means -- print value. So you must specify what value to print.
last line of case must be
_ -> io:format("no match ~p~n",[P])
Besides, io:format returms 'ok'. So if P is not x y or z, your function will return 'ok' instead of numeric value. I would suggest to return tagged value to separate correct and error returns. Kind of
fall_velocity(P, D) when D >= 0 ->
case P of
x -> {ok,math:sqrt(2 * 9.8 * D)};
y -> {ok,math:sqrt(2 * 1.6 * D)};
z -> {ok,math:sqrt(2 * 3.71 * D)};
Otherwise-> io:format("no match:~p~n",[Otherwise]),
{error, "coordinate is not x y or z"}
end.
To make the comments to the other answer explicit, this is how I would write that function:
-module(crop).
-export([fall_velocity/2]).
fall_velocity(P, D) when D >= 0 ->
case P of
x -> math:sqrt(2 * 9.8 * D);
y -> math:sqrt(2 * 1.6 * D);
z -> math:sqrt(2 * 3.71 * D)
end.
That is, don't handle the incorrect argument in your case expression. If someone passes foo as an argument, you'll get the error {case_clause, foo} along with a stacktrace that points to this function and its caller. This also means that this function cannot leak incorrect values into the rest of the code as a result of being called with incorrect arguments.
Returning {ok, Result} | {error, Error} as in the other answer is equally valid. You'll need to choose the variant that fits your case best.

What is wrong with 100000 factorial using ContinuationMonad?

It is powerful technique using recursion because its strong describable feature. Tail recursion provides more powerful computation than normal recursion because it changes recursion into iteration. Continuation-Passing Style (CPS) can change lots of loop codes into tail recursion. Continuation Monad provides recursion syntax but in essence it is tail recursion, which is iteration. It is supposed to reasonable use Continuation Monad for 100000 factorial. Here is the code.
type ContinuationBuilder() =
member b.Bind(x, f) = fun k -> x (fun x -> f x k)
member b.Return x = fun k -> k x
member b.ReturnFrom x = x
(*
type ContinuationBuilder =
class
new : unit -> ContinuationBuilder
member Bind : x:(('d -> 'e) -> 'f) * f:('d -> 'g -> 'e) -> ('g -> 'f)
member Return : x:'b -> (('b -> 'c) -> 'c)
member ReturnFrom : x:'a -> 'a
end
*)
let cont = ContinuationBuilder()
//val cont : ContinuationBuilder
let fac n =
let rec loop n =
cont {
match n with
| n when n = 0I -> return 1I
| _ -> let! x = fun f -> f n
let! y = loop (n - 1I)
return x * y
}
loop n (fun x -> x)
let x2 = fac 100000I
There is wrong message: "Process is terminated due to StackOverflowException."
What is wrong with 100000 factorial using ContinuationMonad?
You need to compile the project in Release mode or check the "Generate tail calls" option in project properties (or use --tailcalls+ if you're running the compiler via command line).
By default, tail call optimization is not enabled in Debug mode. The reason is that, if tail-calls are enabled, you will not see as useful information about stack traces. So, disabling them by default gives you more pleasant debugging experience (even in Debug mode, the compiler optimizes tail-recursive functions that call themselves, which handles most situations).
You probably need to add this memeber to your monad builder:
member this.Delay(mk) = fun c -> mk () c

Resources