NuSMV: difference between IVAR and VAR - models

I know the difference between IVAR (input variables) and VAR (state variables) in NuSMV. However, I am able to understand the counterexample when VAR is used, but I am not in the other case.
Let me show it with an example.
MODULE main
VAR
v1: 0..20;
v2: 0..20;
v3: 0..100;
INIT
v3 = 0;
TRANS
((v2+v1 = 0) -> (next(v3) = 10)) &
(!(v2+v1 = 0) -> (next(v3) = v1 + v2))
LTLSPEC
G(v3 = 10);
The counterexample (clear enough) given by NuSMV is:
Trace Type: Counterexample
-> State: 1.1 <-
v1 = 0
v2 = 0
v3 = 0
-- Loop starts here
-> State: 1.2 <-
v3 = 10
-> State: 1.3 <-
v1 = 7
v2 = 6
-> State: 1.4 <-
v1 = 0
v2 = 0
v3 = 13
-> State: 1.5 <-
v3 = 10
Now, change v1 and v2 to be IVAR.
MODULE main
IVAR
v1: 0..20;
v2: 0..20;
VAR
v3: 0..100;
INIT
v3 = 0;
TRANS
((v2+v1 = 0) -> (next(v3) = 10)) &
(!(v2+v1 = 0) -> (next(v3) = v1 + v2))
LTLSPEC
G(v3 = 10);
The counterexample is:
Trace Type: Counterexample
-> State: 1.1 <-
v3 = 0
-> Input: 1.2 <-
v1 = 7
v2 = 3
-- Loop starts here
-> State: 1.2 <-
v3 = 10
-> Input: 1.3 <-
-- Loop starts here
-> State: 1.3 <-
-> Input: 1.4 <-
-- Loop starts here
-> State: 1.4 <-
-> Input: 1.5 <-
-- Loop starts here
-> State: 1.5 <-
-> Input: 1.6 <-
-- Loop starts here
-> State: 1.6 <-
-> Input: 1.7 <-
-> State: 1.7 <-
Could someone explain why this counterexample is so strange? It has several nested loops. What does the output mean?

Both counter-examples falsify the property in the very first state of the execution trace, so what happens next is somewhat not much relevant.
From the docs of nuXmv:
4.7 Traces
A trace consists of an initial state, optionally followed by a
sequence of states-inputs pairs corresponding to a possible execution
of the model. Apart, from the initial state, each pair contains the
inputs that caused the transition to the new state, and the new state
itself. The initial state has no such input values defined as it does
not depend on the values of any of the inputs. [...]
So a trace generally has the following structure:
S_0 | I_0 -> S_1 | I_1 -> S_2 | ... | I_{N-1} -> S_N
The idea is that a state S_{k+1} is obtained by applying inputs I_k over state S_k.
One can try using the commands goto_state and print_current_state, to navigate the counter-example and print each state's content. Alternatively, one may recall that NuSMV and nuXmv only print changing variables from one state to the next, so the execution trace should look like this:
-> State: 1.1 <-
v3 = 0
-> Input: 1.2 <-
v1 = 7
v2 = 3
-- Loop starts here
-> State: 1.2 <-
v3 = 10
-> Input: 1.3 <-
v1 = 7
v2 = 3
-- Loop starts here
-> State: 1.3 <-
v3 = 10
-> Input: 1.4 <-
v1 = 7
v2 = 3
-- Loop starts here
-> State: 1.4 <-
v3 = 10
-> Input: 1.5 <-
v1 = 7
v2 = 3
-> State: 1.5 <-
v3 = 10
So basically after the first transition we end up in the same state forever with never-changing inputs and outputs.
You may want to contact NuSMV or nuXmv mailing list and mention this issue with the output routine.

Related

Erlang binary to lower case performance

My goal is to speed up performance of ASCII-only binary converting to lower case. I do not need any languages other than English. I've wrote and compare some variants:
Binary comprehension:
binary_comprehension(Binary) ->
<< <<if
C >= $A andalso C =< $Z -> C - $A + $a;
true -> C
end >>
|| <<C>> <= Binary >>.
List comprehension:
list_comprehension(Binary) ->
L = binary_to_list(Binary),
Lower =
[if
C >= $A andalso C =< $Z -> C - $A + $a;
true -> C
end || C <- L],
list_to_binary(Lower).
And regular string:lowercase.
And surprisingly list comprehension beat all others:
1> timer:tc(fun() -> lists:foreach(fun(_) -> tolower:list_comprehension(<<"QWEQWEIQEKQHWKEHKQWHEKQHWKEQWEKHQWLKL">>) end, L100000) end).
{267603,ok}
2> timer:tc(fun() -> lists:foreach(fun(_) -> tolower:binary_comprehension(<<"QWEQWEIQEKQHWKEHKQWHEKQHWKEQWEKHQWLKL">>) end, L100000) end).
{324383,ok}
3> timer:tc(fun() -> lists:foreach(fun(_) -> string:lowercase(<<"QWEQWEIQEKQHWKEHKQWHEKQHWKEQWEKHQWLKL">>) end, L100000) end).
{319819,ok}
Any ideas why double list conversion + comprehension is much faster than just binary transformation?
Maybe you know more powerful optimisation?
Update:
I also found that list-of-char version of string is also fast:
string_lowercase(Binary) ->
L = binary_to_list(Binary),
Lower = string:lowercase(L),
list_to_binary(Lower).
Run:
39> timer:tc(fun() -> lists:foreach(fun(_) -> tolower:string_to_lower(<<"QWEQWEIQEKQHWKEHKQWHEKQHWKEQWEKHQWLKL">>) end, L100000) end).
{277766,ok}
I made some modification of the code and changed test case. Test changes is not mandatory but I personally like more this way:
-module(tolower).
-compile(export_all).
u2l(C) when C >= $A andalso C =< $Z -> C + 32;
u2l(C) -> C.
binary_comprehension(Binary) ->
<< << (u2l(C)) >> || <<C>> <= Binary >>.
list_comprehension(Binary) ->
list_to_binary([u2l(C) || C <- binary_to_list(Binary)]).
list_recur(Binary) -> list_recur(binary_to_list(Binary), []).
list_recur([], Result) -> lists:reverse(Result);
list_recur([C | Tail], Result) when C >= $A andalso C =< $Z ->
list_recur(Tail, [(C + 32) | Result]);
list_recur([C | Tail], Result) ->
list_recur(Tail, [C | Result]).
string_to_lower(Binary) ->
list_to_binary(string:lowercase(binary_to_list(Binary))).
test() ->
L100000 = lists:seq(1, 100000),
TL0 = <<"QWEQWEIQEKQHWKEHKQWHEKQHWKEQWEKHQWLKL">>,
TL = binary:copy(TL0, 100000),
{R0, _} = timer:tc(fun() -> lists:foreach(fun(_) -> tolower:binary_comprehension(TL0) end, L100000) end),
{R1, _} = timer:tc(tolower, binary_comprehension, [TL]),
{R2, _} = timer:tc(tolower, list_comprehension, [TL]),
{R3, _} = timer:tc(tolower, list_recur, [TL]),
{R4, _} = timer:tc(string, lowercase, [TL]),
{R5, _} = timer:tc(tolower, string_to_lower, [TL]),
io:format("~n1.binary_comprehension = ~10w~n2.binary_comprehension = ~10w~n3. list_comprehension = ~10w~n4. list_recur = ~10w~n5. lowercase = ~10w~n6. string_to_lower = ~10w~n",
[R0,R1,R2,R3,R4,R5]).
Erlang shell shows that elapsed time not consistent due to concurrent nature of system. But the best time is for binary_comprehension as expected.
62> c(tolower).
tolower.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,tolower}
63> l(tolower).
{module,tolower}
64> tolower:test().
1.binary_comprehension = 109000
2.binary_comprehension = 94000
3. list_comprehension = 312001
4. list_recur = 344001
5. lowercase = 469002
6. string_to_lower = 218000
ok
65> tolower:test().
1.binary_comprehension = 140998
2.binary_comprehension = 93999
3. list_comprehension = 327994
4. list_recur = 296996
5. lowercase = 155997
6. string_to_lower = 280996
ok
66> tolower:test().
1.binary_comprehension = 124998
2.binary_comprehension = 93998
3. list_comprehension = 327995
4. list_recur = 296995
5. lowercase = 452993
6. string_to_lower = 202997
ok
67> tolower:test().
1.binary_comprehension = 125000
2.binary_comprehension = 94000
3. list_comprehension = 312000
4. list_recur = 282000
5. lowercase = 171000
6. string_to_lower = 266000
ok
Time on line 5 is different of time on line 6 because when you invoke string:lowercase/1 with binary argument it processed as utf8 sequence. When you invoke string:lowercase/1 with string argument utf8 processing is avoided. See code of string.erl from OTP for details.

NuSMV model checking: create a simple game model

I'm new to NuSMV and try to model this simple turn-based game. There are 10 bricks in a pile, each player can take 1-3 brick per turn, whoever take the last brick wins the game. Assume player A go first, and here is my attempt. I want to express that "eventually there is a winner",but my code doesn't work because it does not prevent player taking brick after brick=0, so eventually player a,b will both become winner.
here is my code:
MODULE main
VAR
bricks : 0..10;
i : 1..3;
j : 1..3;
turn : boolean;
winner : {none, a, b};
ASSIGN
init(winner) := none;
init(bricks) := 10;
init(turn) := TRUE;
next(turn) := case
turn : FALSE;
!turn: TRUE;
esac;
next(bricks) :=
case
bricks - j >= 0 : bricks - j;
bricks - j < 0 : 0;
TRUE:bricks;
esac;
next(winner) := case
turn=TRUE & bricks = 0: a;
turn=FALSE & bricks = 0: b;
TRUE:winner;
esac;
SPEC AF (winner = a | winner = b)
and here is my output on SPEC AF (winner = a | winner = none) to illustrate my point.
i = 1
j = 1
turn = TRUE
winner = none
State: 1.2 <-
bricks = 9
j = 3
turn = FALSE
State: 1.3 <-
bricks = 6
turn = TRUE
State: 1.4 <-
bricks = 3
turn = FALSE
State: 1.5 <-
bricks = 0
j = 1
turn = TRUE
State: 1.6 <-
turn = FALSE
winner = a
State: 1.7 <-
turn = TRUE
winner = b
as you can see, model still provide a counter example where player b win the game after playe a already won.
I am not sure how you provided a counter-example, since the property you specified is verified by the model:
-- specification AF (winner = a | winner = b) is true
Perhaps you simulated the program and simply observed that it behaves in an unexpected manner. The property that you seem to really want to verify is AF (AG winner = a | AG winner = b). In fact, using this property results in a counter-example similar to your own:
-- specification AF (AG winner = a | AG winner = b) is false
-- as demonstrated by the following execution sequence
Trace Description: CTL Counterexample
Trace Type: Counterexample
-> State: 1.1 <-
bricks = 10
i = 1
j = 1
turn = TRUE
winner = none
-> State: 1.2 <-
bricks = 9
turn = FALSE
-> State: 1.3 <-
bricks = 8
turn = TRUE
-> State: 1.4 <-
bricks = 7
turn = FALSE
-> State: 1.5 <-
bricks = 6
turn = TRUE
-> State: 1.6 <-
bricks = 5
turn = FALSE
-> State: 1.7 <-
bricks = 4
turn = TRUE
-> State: 1.8 <-
bricks = 3
turn = FALSE
-> State: 1.9 <-
bricks = 2
turn = TRUE
-> State: 1.10 <-
bricks = 1
turn = FALSE
-> State: 1.11 <-
bricks = 0
turn = TRUE
-- Loop starts here
-> State: 1.12 <-
turn = FALSE
winner = a
-> State: 1.13 <-
turn = TRUE
winner = b
-> State: 1.14 <-
turn = FALSE
winner = a
The problem is that you flip turns even when the game is finished, and as a result of this, the winner continuously flips among A and B too.
I re-wrote your solution in a better way:
MODULE main
VAR
bricks : 0..10;
q : 0..3;
turn : {A_TURN , B_TURN};
DEFINE
game_won := next(bricks) = 0;
a_won := game_won & turn = A_TURN;
b_won := game_won & turn = B_TURN;
ASSIGN
init(bricks) := 10;
init(turn) := A_TURN;
next(bricks) := case
bricks - q >= 0 : bricks - q;
TRUE : 0;
esac;
next(turn) := case
turn = A_TURN & !game_won: B_TURN;
turn = B_TURN & !game_won: A_TURN;
TRUE : turn;
esac;
-- forbid q values from being both larger than the remaining number of
-- bricks, and equal to zero when there are still bricks to take.
INVAR (q <= bricks)
INVAR (bricks > 0) -> (q > 0)
INVAR (bricks <= 0) -> (q = 0)
-- Sooner or later the number of bricks will always be
-- zero for every possible state in every possible path,
-- that is, someone won the game
CTLSPEC
AF AG (bricks = 0)
I think the code is quite self-explanatory.
You can run it with both NuSMV and nuXmv using the following commands:
> read_model -i game.smv
> go
> check_property
-- specification AF (AG bricks = 0) is true
If instead you want to find a possible solution, just flip the property:
> check_ctlspec -p "AF AG (bricks != 0)"
-- specification AF (AG bricks != 0) is false
-- as demonstrated by the following execution sequence
Trace Description: CTL Counterexample
Trace Type: Counterexample
-> State: 1.1 <-
bricks = 10
q = 1
turn = A_TURN
game_won = FALSE
b_won = FALSE
a_won = FALSE
-> State: 1.2 <-
bricks = 9
turn = B_TURN
-> State: 1.3 <-
bricks = 8
turn = A_TURN
-> State: 1.4 <-
bricks = 7
turn = B_TURN
-> State: 1.5 <-
bricks = 6
turn = A_TURN
-> State: 1.6 <-
bricks = 5
turn = B_TURN
-> State: 1.7 <-
bricks = 4
turn = A_TURN
-> State: 1.8 <-
bricks = 3
turn = B_TURN
-> State: 1.9 <-
bricks = 2
turn = A_TURN
-> State: 1.10 <-
bricks = 1
turn = B_TURN
game_won = TRUE
b_won = TRUE
-- Loop starts here
-> State: 1.11 <-
bricks = 0
q = 0
-> State: 1.12 <-
I hope you'll find this answer helpful.

Why is the "better" digit-listing function slower?

I was playing around with Project Euler #34, and I wrote these functions:
import Data.Time.Clock.POSIX
import Data.Char
digits :: (Integral a) => a -> [Int]
digits x
| x < 10 = [fromIntegral x]
| otherwise = let (q, r) = x `quotRem` 10 in (fromIntegral r) : (digits q)
digitsByShow :: (Integral a, Show a) => a -> [Int]
digitsByShow = map (\x -> ord x - ord '0') . show
I thought that for sure digits has to be the faster one, as we don't convert to a String. I could not have been more wrong. I ran the two versions via pe034:
pe034 digitFunc = sum $ filter sumFactDigit [3..2540160]
where
sumFactDigit :: Int -> Bool
sumFactDigit n = n == (sum $ map sFact $ digitFunc n)
sFact :: Int -> Int
sFact n
| n == 0 = 1
| n == 1 = 1
| n == 2 = 2
| n == 3 = 6
| n == 4 = 24
| n == 5 = 120
| n == 6 = 720
| n == 7 = 5040
| n == 8 = 40320
| n == 9 = 362880
main = do
begin <- getPOSIXTime
print $ pe034 digitsByShow -- or digits
end <- getPOSIXTime
print $ end - begin
After compiling with ghc -O, digits consistently takes .5 seconds, while digitsByShow consistently takes .3 seconds. Why is this so? Why is the function which stays within Integer arithmetic slower, whereas the function which goes into string comparison is faster?
I ask this because I come from programming in Java and similar languages, where the % 10 trick of generating digits is way faster than the "convert to String" method. I haven't been able to wrap my head around the fact that converting to a string could be faster.
This is the best I can come up with.
digitsV2 :: (Integral a) => a -> [Int]
digitsV2 n = go n []
where
go x xs
| x < 10 = fromIntegral x : xs
| otherwise = case quotRem x 10 of
(q,r) -> go q (fromIntegral r : xs)
when compiled with -O2 and tested with Criterion
digits runs in 470.4 ms
digitsByShow runs in 421.8 ms
digitsV2 runs in 258.0 ms
results may vary
edit:
I am not sure why building the list like this helps so much.
But you can improve your codes speed by strictly evaluating quotRem x 10
You can do this with BangPatterns
| otherwise = let !(q, r) = x `quotRem` 10 in (fromIntegral r) : (digits q)
or with case
| otherwise = case quotRem x 10 of
(q,r) -> fromIntegral r : digits q
Doing this drops digits down to 323.5 ms
edit: time without using Criterion
digits = 464.3 ms
digitsStrict = 328.2 ms
digitsByShow = 259.2 ms
digitV2 = 252.5 ms
note: The criterion package measures software performance.
Let's investigate why #No_signal's solution is faster.
I made three runs of ghc:
ghc -O2 -ddump-simpl digits.hs >digits.txt
ghc -O2 -ddump-simpl digitsV2.hs >digitsV2.txt
ghc -O2 -ddump-simpl show.hs >show.txt
digits.hs
digits :: (Integral a) => a -> [Int]
digits x
| x < 10 = [fromIntegral x]
| otherwise = let (q, r) = x `quotRem` 10 in (fromIntegral r) : (digits q)
main = return $ digits 1
digitsV2.hs
digitsV2 :: (Integral a) => a -> [Int]
digitsV2 n = go n []
where
go x xs
| x < 10 = fromIntegral x : xs
| otherwise = let (q, r) = x `quotRem` 10 in go q (fromIntegral r : xs)
main = return $ digits 1
show.hs
import Data.Char
digitsByShow :: (Integral a, Show a) => a -> [Int]
digitsByShow = map (\x -> ord x - ord '0') . show
main = return $ digitsByShow 1
If you'd like to view the complete txt files, I placed them on ideone (rather than paste a 10000 char dump here):
digits.txt
digitsV2.txt
show.txt
If we carefully look through digits.txt, it appears that this is the relevant section:
lvl_r1qU = __integer 10
Rec {
Main.$w$sdigits [InlPrag=[0], Occ=LoopBreaker]
:: Integer -> (# Int, [Int] #)
[GblId, Arity=1, Str=DmdType <S,U>]
Main.$w$sdigits =
\ (w_s1pI :: Integer) ->
case integer-gmp-1.0.0.0:GHC.Integer.Type.ltInteger#
w_s1pI lvl_r1qU
of wild_a17q { __DEFAULT ->
case GHC.Prim.tagToEnum# # Bool wild_a17q of _ [Occ=Dead] {
False ->
let {
ds_s16Q [Dmd=<L,U(U,U)>] :: (Integer, Integer)
[LclId, Str=DmdType]
ds_s16Q =
case integer-gmp-1.0.0.0:GHC.Integer.Type.quotRemInteger
w_s1pI lvl_r1qU
of _ [Occ=Dead] { (# ipv_a17D, ipv1_a17E #) ->
(ipv_a17D, ipv1_a17E)
} } in
(# case ds_s16Q of _ [Occ=Dead] { (q_a11V, r_X12h) ->
case integer-gmp-1.0.0.0:GHC.Integer.Type.integerToInt r_X12h
of wild3_a17c { __DEFAULT ->
GHC.Types.I# wild3_a17c
}
},
case ds_s16Q of _ [Occ=Dead] { (q_X12h, r_X129) ->
case Main.$w$sdigits q_X12h
of _ [Occ=Dead] { (# ww1_s1pO, ww2_s1pP #) ->
GHC.Types.: # Int ww1_s1pO ww2_s1pP
}
} #);
True ->
(# GHC.Num.$fNumInt_$cfromInteger w_s1pI, GHC.Types.[] # Int #)
}
}
end Rec }
digitsV2.txt:
lvl_r1xl = __integer 10
Rec {
Main.$wgo [InlPrag=[0], Occ=LoopBreaker]
:: Integer -> [Int] -> (# Int, [Int] #)
[GblId, Arity=2, Str=DmdType <S,U><L,U>]
Main.$wgo =
\ (w_s1wh :: Integer) (w1_s1wi :: [Int]) ->
case integer-gmp-1.0.0.0:GHC.Integer.Type.ltInteger#
w_s1wh lvl_r1xl
of wild_a1dp { __DEFAULT ->
case GHC.Prim.tagToEnum# # Bool wild_a1dp of _ [Occ=Dead] {
False ->
case integer-gmp-1.0.0.0:GHC.Integer.Type.quotRemInteger
w_s1wh lvl_r1xl
of _ [Occ=Dead] { (# ipv_a1dB, ipv1_a1dC #) ->
Main.$wgo
ipv_a1dB
(GHC.Types.:
# Int
(case integer-gmp-1.0.0.0:GHC.Integer.Type.integerToInt ipv1_a1dC
of wild2_a1ea { __DEFAULT ->
GHC.Types.I# wild2_a1ea
})
w1_s1wi)
};
True -> (# GHC.Num.$fNumInt_$cfromInteger w_s1wh, w1_s1wi #)
}
}
end Rec }
I actually couldn't find the relevant section for show.txt. I'll work on that later.
Right off the bat, digitsV2.hs produces shorter code. That's probably a good sign for it.
digits.hs seems to be following this psuedocode:
def digits(w_s1pI):
if w_s1pI < 10: return [fromInteger(w_s1pI)]
else:
ds_s16Q = quotRem(w_s1pI, 10)
q_X12h = ds_s16Q[0]
r_X12h = ds_s16Q[1]
wild3_a17c = integerToInt(r_X12h)
ww1_s1pO = r_X12h
ww2_s1pP = digits(q_X12h)
ww2_s1pP.pushFront(ww1_s1pO)
return ww2_s1pP
digitsV2.hs seems to be following this psuedocode:
def digitsV2(w_s1wh, w1_s1wi=[]): # actually disguised as go(), as #No_signal wrote
if w_s1wh < 10:
w1_s1wi.pushFront(fromInteger(w_s1wh))
return w1_s1wi
else:
ipv_a1dB, ipv1_a1dC = quotRem(w_s1wh, 10)
w1_s1wi.pushFront(integerToIn(ipv1a1dC))
return digitsV2(ipv1_a1dC, w1_s1wi)
It might not be that these functions mutate lists like my psuedocode suggests, but this immediately suggests something: it looks as if digitsV2 is fully tail-recursive, whereas digits is actually not (may have to use some Haskell trampoline or something). It appears as if Haskell needs to store all the remainders in digits before pushing them all to the front of the list, whereas it can just push them and forget about them in digitsV2. This is purely speculation, but it is well-founded speculation.

3n+1 implementing with Haskell, compile error

everyone. I'm a newcomer to Haskell and just implemented the '3n + 1' problem with it. I checked a lot but the type error seemed strange, could you please help me find what the problem is?
import qualified Data.Vector as V
import qualified Data.Matrix as M
nMax = 1000000
table = V.fromList $ 0 : 1 : [cycleLength x | x <- [2 .. nMax]] where
cycleLength x = if x' <= nMax then table V.! x' + 1 else cycleLength x' + 1 where
x' = if even x then x `div` 2 else 3 * x + 1
sparseTable = M.fromLists $ [] : [[f i j | j <- [0 .. ceiling $ logBase 2 nMax]] | i <- [1 .. nMax]] where
f i 0 = table V.! i
f i j = maxValue i j
maxValue i j = max $ (leftValue i j) (rightValue i j) where
leftValue i j = sparseTable M.! (i, j - 1)
rightValue i j = sparseTable M.! (i + 2 ^ (j - 1), j - 1)
I used the Vector and Matrix (download with cabal) modules to implement the functions. I think the first function (table) has been proved that no mistakes in it, probably mistakes are in the last two function, which I used to implement the sparse table algorithm.
Since I just signed up and don't have enough reputation now, I just paste the error message here:
[1 of 1] Compiling Main ( 001.hs, interpreted )
001.hs:14:39:
Occurs check: cannot construct the infinite type: s0 ~ s0 -> s0
Relevant bindings include
leftValue :: Int -> Int -> s0 -> s0 (bound at 001.hs:15:9)
rightValue :: Int -> Int -> s0 -> s0 (bound at 001.hs:16:9)
maxValue :: Int -> Int -> s0 -> s0 (bound at 001.hs:14:1)
In the third argument of ‘leftValue’, namely ‘(rightValue i j)’
In the second argument of ‘($)’, namely
‘(leftValue i j) (rightValue i j)’
Failed, modules loaded: none.
The problem is the $ in max $ (leftValue i j) (rightValue i j).
The ($) operator binds less tightly than any other operator, including the 'normal function application you get when you just use a space.
So with the $, it parses as
max ((leftvalue i j) (rightValue i j))
if you remove it that should parse as you intended, which was presumably
max (leftValue i j) (rightValue i j)
You can get a hint of this from the error message, where it talks about the "third argument of leftValue".
There's some more information about ($) in When should I use $ (and can it always be replaced with parentheses)?

Fusion optimization with intermediate values

Will GHC transform an expression with intermediate values as efficiently as one without?
e.g.
main = print $ f ["aa", "bb", "cc"]
f x =
let a = map (map toUpper) x
b = filter (\z -> 'C' /= head z) a
c = foldl1 (++) b
in c
seems to have very different core output (with -ddump-simple) than with
f x = foldl1 (++) $ filter (\z -> 'C' /= head z) $ map (map toUpper) x
Could an expression with intermediate values take (significantly) longer to evaluate?
Linear use of intermediate let bindings is equivalent to putting (.) between the values.
GHC will fuse through such pipelines. You can see from the results of -ddump-simpl-stats
With let Bindings:
15 RuleFired
1 ++
1 Class op /=
1 Class op show
1 Class op showList
1 filter
1 fold/build
1 foldr/app
1 map
1 neChar#->case
3 unpack
3 unpack-list
Using a pipeline:
15 RuleFired
1 ++
1 Class op /=
1 Class op show
1 Class op showList
1 filter
1 fold/build
1 foldr/app
1 map
1 neChar#->case
3 unpack
3 unpack-list
And the same fused worker:
With let Bindings:
Main.main_go =
\ (ds_aAz :: [[GHC.Types.Char]]) ->
case ds_aAz of _ {
[] -> GHC.Types.[] # [GHC.Types.Char];
: y_aAE ys_aAF ->
case GHC.Base.map
# GHC.Types.Char # GHC.Types.Char GHC.Unicode.toUpper y_aAE
of wild1_azI {
[] ->
GHC.List.badHead
`cast` (UnsafeCo (forall a_azK. a_azK) [[GHC.Types.Char]]
:: (forall a_azK. a_azK) ~ [[GHC.Types.Char]]);
: x_azM ds1_azN ->
case x_azM of _ { GHC.Types.C# c2_aAa ->
case c2_aAa of _ {
__DEFAULT ->
GHC.Types.: # [GHC.Types.Char] wild1_azI (Main.main_go ys_aAF);
'C' -> Main.main_go ys_aAF
}
Pipeline:
Main.main_go =
\ (ds_aAA :: [[GHC.Types.Char]]) ->
case ds_aAA of _ {
[] -> GHC.Types.[] # [GHC.Types.Char];
: y_aAF ys_aAG ->
case GHC.Base.map
# GHC.Types.Char # GHC.Types.Char GHC.Unicode.toUpper y_aAF
of wild1_azB {
[] ->
GHC.List.badHead
`cast` (UnsafeCo (forall a_azD. a_azD) [[GHC.Types.Char]]
:: (forall a_azD. a_azD) ~ [[GHC.Types.Char]]);
: x_azF ds1_azG ->
case x_azF of _ { GHC.Types.C# c2_aA3 ->
case c2_aA3 of _ {
__DEFAULT ->
GHC.Types.: # [GHC.Types.Char] wild1_azB (Main.main_go ys_aAG);
'C' -> Main.main_go ys_aAG
}
}
Did you forget to compile with -O2 ?

Resources