lookupDefault

Data.HashMap.Strict

lookupDefault :: (Eq k, Hashable k) => v -> k -> HashMap k v -> v lookupDefault def k mp = ...

k

mp

k

def

def

k

mp

lookupDefault

def

lookupDefault

def

lookupDefault

lookupDefault (error "Uniplate internal error: Report to Neil Mitchell, couldn't grab in follower") x mp

x

mp

error

x

mp

lookupDefault

error

fromMaybe (error "Uniplate internal error: Report to Neil Mitchell, couldn't grab in follower") $ lookup x mp

lookupDefault (hit ! x) x mp

x

mp

x

hit

hit

lookupDefault

fromMaybe

lookupDefault

lookupDefault

"You are using Data.HashMap.Strict" - the Strict module for a data structure should provide a data structure containing strict values, not a module of functions which are unnecessarily strict - values which are never stored in the data structure should never be evaluated. As another example consider the Control.Monad.State.Strict module, which provides a strict state monad. In that module, the state is strict, but functions like return are not needlessly strict.

module, which provides a strict state monad. In that module, the state is strict, but functions like are not needlessly strict. "We don't support using undefined values" - I expect all Haskell libraries to work with undefined values where sensible, not be corrupted in the presence of exceptions and be multithread safe. These are some of the attributes that are essential to allow libraries to be used predictably and reused in situations the authors did not consider.

"Strictness is faster" - a strict lookupDefault may occasionally shave nanoseconds off a program, but can make a program arbitrarily slower, and in the case of HLint makes a commonly used program 10 times slower.

may occasionally shave nanoseconds off a program, but can make a program arbitrarily slower, and in the case of HLint makes a commonly used program 10 times slower. "It is what people expect" - I didn't expect lookupDefault to be strict, despite being one of a handful of Haskell programmers to program in a strict variant of Haskell all day long.

I've just uploaded uniplate-1.6.11 , which fixes a severe performance regression introduced by unordered-containers-0.2.3.0 and above. As an example, the time to run HLint on the HLint source code ( my standard benchmark ) jumped from 1.7 seconds to 18.6 seconds, more than ten times slower. I strongly recommend anyone using Uniplate/HLint to upgrade.The problem is caused by thefunction from themodule:This function looks upin, but ifis not found, returns. There has been discussion over whethershould be evaluated even ifis present in, and since unordered-containers-0.2.3.0always evaluates. There are many legitimate discussions on semantics in the Haskell community, but I do not consider this discussion to be one of them -should not pointlessly evaluate. As John Hughes elegantly argues in "Why Functional Programming Matters" , laziness lets you write functions that composable properly. I have spent several days debugging two problems caused by the excessive strictness inuniplate-1.6.9 contained the following code:Uniplate uses a very complex and highly tuned algorithm to determine which values can be recursively contained by a type. The algorithm relies on subtle invariants, in particular thatmust be a member ofat this particular point. If that fails for certain data types, I want users to submit a bug report, rather than wonder if they called Uniplate incorrectly. The simplest way to achieve that goal is using a default value ofthat is only evaluated ifis not present in. Unfortunately, by makingstrict, this error message was always triggered. People have argued that passingas the default is only to work around the absence of stack traces in GHC - an argument disproven by the example above. The error message provides information that would not be provided by a stack trace - the error is not merely an error, but an error that is my fault. I fixed this bug in uniplate-1.6.10 by switching to:uniplate-1.6.10 contained the following code:In this example I look upin, but if that fails, look upin. By forcing the default to be evaluated this code always performs the lookup in, resulting in a slowdown of more than 10 times in HLint, and more than 20 times in Uniplate microbenchmarks. I fixed this bug in February by changing all instances ofto useafter finding outwas incorrect, but was unaware of the significant performance impact until earlier today.There are various arguments for makingstrict in the default argument: