Created
October 14, 2017 23:09
-
-
Save phadej/1d04208a84f234778e309708f207e9af to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Compile with | |
-- ghc -Wall -O -fforce-recomp -ddump-simpl -dsuppress-all poly.hs | |
{-# LANGUAGE BangPatterns #-} | |
{-# LANGUAGE DataKinds #-} | |
{-# LANGUAGE KindSignatures #-} | |
{-# LANGUAGE PolyKinds #-} | |
{-# LANGUAGE ScopedTypeVariables #-} | |
module Poly (value, ualue, xalue) where | |
import Data.Proxy (Proxy (..)) | |
data N = Z | S N | |
type N5 = 'S ('S ('S ('S ('S 'Z)))) | |
type N7 = 'S ('S N5) | |
type Bound = N5 | |
-- Dictionary inlines better if it's single value. ¯\_(ツ)_/¯ | |
class Foldl (n :: N) where | |
left :: proxy n -> (b -> a -> b) -> b -> [a] -> b | |
instance Foldl 'Z where | |
left _ f z = go z where | |
go !acc [] = acc | |
go !acc (x : xs) = go (f acc x) xs | |
instance Foldl n => Foldl ('S n) where | |
left _ _ z [] = z | |
left _ f z (x : xs) = left (Proxy :: Proxy n) f (f z x) xs | |
foldl' :: forall a b. (b -> a -> b) -> b -> [a] -> b | |
foldl' = left (Proxy :: Proxy Bound) | |
-- | Core: | |
-- | |
-- @ | |
-- -- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0} | |
-- value | |
-- value = I# 10# | |
-- @ | |
-- | |
value :: Int | |
value = foldl' (+) 0 [1,2,3,4] | |
-- This doesn't work out because 'enumFromTo' is recursive :( | |
ualue :: Int | |
ualue = foldl' (+) 0 [1..100] | |
------------------------------------------------------------------------------- | |
-- Second try | |
------------------------------------------------------------------------------- | |
class Unfoldr (n :: N) where | |
unf :: proxy n -> (b -> Maybe (a, b)) -> b -> [a] | |
instance Unfoldr 'Z where | |
unf _ f = go where | |
go z = case f z of | |
Nothing -> [] | |
Just (a, b) -> a : go b | |
instance Unfoldr n => Unfoldr ('S n) where | |
unf _ f z = case f z of | |
Nothing -> [] | |
Just (a, b) -> a : unf (Proxy :: Proxy n) f b | |
unfoldr :: (b -> Maybe (a, b)) -> b -> [a] | |
unfoldr = unf (Proxy :: Proxy Bound) | |
-- | Core; | |
-- | |
-- @ | |
-- -- RHS size: {terms: 8, types: 1, coercions: 0, joins: 0/0} | |
-- xalue | |
-- xalue | |
-- = case $wgo2 15# ($wgo1 6#) of ww_s2ZA { __DEFAULT -> I# ww_s2ZA } | |
-- | |
-- Rec { | |
-- -- RHS size: {terms: 22, types: 7, coercions: 0, joins: 0/0} | |
-- $wgo1 | |
-- $wgo1 | |
-- = \ ww_s2Zo -> | |
-- case tagToEnum# (># ww_s2Zo 100#) of { | |
-- False -> | |
-- : (I# ww_s2Zo) | |
-- (case ww_s2Zo of wild1_a2Ny { | |
-- __DEFAULT -> $wgo1 (+# wild1_a2Ny 1#); | |
-- 9223372036854775807# -> case $fEnumInt2 of wild2_00 { } | |
-- }); | |
-- True -> [] | |
-- } | |
-- end Rec } | |
-- | |
-- Rec { | |
-- -- RHS size: {terms: 15, types: 10, coercions: 0, joins: 0/0} | |
-- $wgo2 | |
-- $wgo2 | |
-- = \ ww_s2Zf w_s2Zc -> | |
-- case w_s2Zc of { | |
-- [] -> ww_s2Zf; | |
-- : x_aXn xs_aXo -> | |
-- case x_aXn of { I# y_X2NU -> $wgo2 (+# ww_s2Zf y_X2NU) xs_aXo } | |
-- } | |
-- end Rec } | |
-- @ | |
xalue :: Int | |
xalue = foldl' (+) 0 (myEnumFromTo 1 100) | |
myEnumFromTo :: Enum a => a -> a -> [a] | |
myEnumFromTo x y = unfoldr f (fromEnum x) where | |
y' = fromEnum y | |
f z | z > y' = Nothing | |
| otherwise = Just (toEnum z, succ z) | |
------------------------------------------------------------------------------- | |
-- Bonus | |
------------------------------------------------------------------------------- | |
class Foldr (n :: N) where | |
right :: proxy n -> (a -> b -> b) -> b -> [a] -> b | |
instance Foldr 'Z where | |
right _ f z = go where | |
go [] = z | |
go (x : xs) = f x (go xs) | |
instance Foldr n => Foldr ('S n) where | |
right _ _ z [] = z | |
right _ f z (x : xs) = f x (right (Proxy :: Proxy n) f z xs) |
@nrolland yes, you read it exactly right. (Gists don't give me notifications, sorry for very late response)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Trying to get it : the first example shows that if we know the list is at least of some size n, we can unroll statically the n recursive calls from the base case. instead of having
foldl z s xs
we (statically) rewrite it, say,foldl (s (s (s z x1) x2) x3) s xs
, so the optimizer will inline thes
calls and compute the first value statically.It the case of a structure coming from an unfold (like
[1..100]
), this fails as it is defined recursively at value level. We do the same process of inlining as much as is known at type level, replacingunfoldr f x
byx:(s x):(s (s x)):unfoldr (s (s (x)))
.The compiler can now optimize the overall computation in
xvalue
.The information was thrown away in the result type of
myEnumFromTo
, but it does not prevent the simplification to happen.Is it a correct reading of what is going on ? (ps : Interesting example, thank you for sharing it)