Skip to content

Commit

Permalink
Make foldl strict, like foldl'
Browse files Browse the repository at this point in the history
  • Loading branch information
martijnbastiaan committed Mar 11, 2024
1 parent aff963f commit 56a5f33
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 3 deletions.
1 change: 1 addition & 0 deletions changelog/2024-03-09T15_02_17+01_00_fix_2482
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FIXED: `foldl` on `Vec` is now strict. See [this GitHub discussion](https://github.com/hasura/graphql-engine/pull/2933#discussion_r328821960) and [this blog post](https://well-typed.com/blog/90/) for more information. [#2482](https://github.com/clash-lang/clash-compiler/issues/2482)
24 changes: 21 additions & 3 deletions clash-prelude/src/Clash/Sized/Vector.hs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ import Unsafe.Coerce (unsafeCoerce)

import Clash.Annotations.Primitive
(Primitive(InlineYamlPrimitive), HDL(..), dontTranslate, hasBlackBox)
import Clash.Magic (clashCompileError)
import Clash.Magic (clashCompileError, clashSimulation)
import Clash.Promoted.Nat
(SNat (..), SNatLE (..), UNat (..), compareSNat, pow2SNat,
snatProxy, snatToInteger, subSNat, withSNat, toUNat, natToInteger)
Expand Down Expand Up @@ -1058,8 +1058,26 @@ foldr f z (x `Cons` xs) = f x (foldr f z xs)
-- delay, of O(@'length' xs@). Use 'fold' if your binary operator @f@ is
-- associative, as @"'fold' f xs"@ produces a structure with a depth of
-- O(log_2(@'length' xs@)).
foldl :: (b -> a -> b) -> b -> Vec n a -> b
foldl f z xs = last (scanl f z xs)
foldl :: forall b a n . (b -> a -> b) -> b -> Vec n a -> b
foldl f z0 xs0
-- We use 'go' to make 'foldl' strict during Haskell simulation, while using
-- 'scanl' to make Clash's life easier during normalization. We make 'foldl'
-- strict due to similar reasoning to:
--
-- https://well-typed.com/blog/90/
--
-- Also see:
--
-- https://github.com/hasura/graphql-engine/pull/2933#discussion_r328821960
--
| clashSimulation = go z0 xs0
| otherwise = last (scanl f z0 xs0)
where
go :: forall m. b -> Vec m a -> b
go z Nil = z
go z (Cons x xs) =
let z1 = f z x
in z1 `seq` go z1 xs
{-# INLINE foldl #-}

-- | 'foldr1' is a variant of 'foldr' that has no starting value argument,
Expand Down

0 comments on commit 56a5f33

Please sign in to comment.