|
1 | 1 | {-# LANGUAGE CPP, MagicHash, UnboxedTuples, DeriveDataTypeable, BangPatterns #-} |
2 | 2 | {-# LANGUAGE RankNTypes #-} |
3 | 3 | {-# LANGUAGE TypeFamilies #-} |
4 | | -#if __GLASGOW_HASKELL__ >= 706 |
5 | | -{-# LANGUAGE PolyKinds #-} |
6 | | -#endif |
7 | 4 |
|
8 | 5 | -- | |
9 | 6 | -- Module : Data.Primitive.Array |
@@ -344,6 +341,7 @@ emptyArray# _ = case emptyArray of Array ar -> ar |
344 | 341 | {-# NOINLINE emptyArray# #-} |
345 | 342 | #endif |
346 | 343 |
|
| 344 | + |
347 | 345 | die :: String -> String -> a |
348 | 346 | die fun problem = error $ "Data.Primitive.Array." ++ fun ++ ": " ++ problem |
349 | 347 |
|
@@ -818,24 +816,72 @@ runArrays m = runST $ m >>= traverse unsafeFreezeArray |
818 | 816 | -- constraint. To produce arrays of varying types, use 'runArraysHetOf'. |
819 | 817 | runArraysOf |
820 | 818 | :: (forall s1 s2. |
821 | | - (MutableArray s1 a -> ST s2 (Array a)) -> t (MutableArray s1 a) -> ST s2 (u (Array a))) |
822 | | - -> (forall s. ST s (t (MutableArray s a))) |
823 | | - -> u (Array a) |
| 819 | + (MutableArray s1 a -> ST s2 (Array a)) -> t (mut s1 x) -> ST s2 u) |
| 820 | + -> (forall s. ST s (t (mut s x))) |
| 821 | + -> u |
| 822 | +-- See notes below |
824 | 823 | runArraysOf trav m = runST $ m >>= trav unsafeFreezeArray |
825 | 824 |
|
826 | 825 | {- |
| 826 | +Why do I believe 'runArraysOf' is safe? The key safety property is |
| 827 | +that we must never modify an array after it is frozen. The first |
| 828 | +thing we do is run the given action, producing something of type |
| 829 | +
|
| 830 | + t (mut s x) |
| 831 | +
|
| 832 | +and passing it to trav. We need to make sure that trav just applies |
| 833 | +its function argument (unsafeFreezeArray) to any MutableArrays that |
| 834 | +may contain/produce, and doesn't modify them in any other ways. Consider |
| 835 | +the type of trav: |
| 836 | +
|
| 837 | + trav :: forall s1 s2. |
| 838 | + (MutableArray s1 a -> ST s2 (Array a)) |
| 839 | + -> t (mut s1 x) -> ST s2 u |
| 840 | +
|
| 841 | +trav operates in the state thread labeled s2. We don't let it know that |
| 842 | +the mutable arrays it handles live in the same thread! They're off in |
| 843 | +s1, a whole different universe. So trav can only apply the freeze it's |
| 844 | +passed, or perform whatever actions may ride in on t (mut s x). Can |
| 845 | +the latter happen? Imagine something like |
| 846 | +
|
| 847 | + data T :: Type -> Type where |
| 848 | + T :: ST s (MutableArray s x) -> T (MutableArray s x) |
| 849 | +
|
| 850 | +Can trav pull this open and run the action? No! The state thread in |
| 851 | +T matches the array in T, but it doesn't match the state thread trav |
| 852 | +lives in, so trav can't do anything whatsoever with it. |
| 853 | +
|
| 854 | +----- |
| 855 | +
|
| 856 | +It's annoying that @t@ takes a @mut s1 x@ argument instead |
| 857 | +of just an @s1@ argument, but this allows 'runArraysOf' to be used directly |
| 858 | +with 'traverse'. The cleaner version can be implemented efficiently on |
| 859 | +top in the following rather disgusting manner: |
| 860 | +
|
| 861 | +runArraysOf' |
| 862 | + :: (forall s1 s2. |
| 863 | + (MutableArray s1 a -> ST s2 (Array a)) -> t s1 -> ST s2 u) |
| 864 | + -> (forall s. ST s (t s)) |
| 865 | + -> u |
| 866 | +runArraysOf' trav m = runArraysOf ((. unBar) #. trav) (coerce m) |
| 867 | +
|
| 868 | +newtype Bar t x = Bar {unBar :: t (Yuck x)} |
| 869 | +type family Yuck x where |
| 870 | + Yuck (_ s _) = s |
| 871 | +
|
| 872 | +------- |
| 873 | +
|
827 | 874 | I initially thought we'd need a function like |
828 | 875 |
|
829 | 876 | runArraysOfThen |
830 | 877 | :: (forall s1 s2. |
831 | | - (MutableArray s1 a -> Compose (ST s2) q r) -> t (MutableArray s1 a) -> Compose (ST s2) q (u r)) |
| 878 | + (MutableArray s1 a -> Compose (ST s2) q r) -> t (MutableArray s1 a) -> Compose (ST s2) q u) |
832 | 879 | -> (Array a -> q r) |
833 | 880 | -> (forall s. ST s (t (MutableArray s a))) |
834 | | - -> q (u r) |
| 881 | + -> q u |
835 | 882 |
|
836 | | -to allow users to traverse over the generated arrays. But because 'runArraysOf' |
837 | | -allows the traversal function to know that it is producing values of type |
838 | | -@Array a@, one could just write |
| 883 | +to allow users to traverse over the generated arrays. But in fact, |
| 884 | +one could just write |
839 | 885 |
|
840 | 886 | runArraysOfThen trav post m = getConst $ |
841 | 887 | runArraysOf (\f -> coerce . getCompose . (trav (Compose . fmap post . f))) m |
@@ -886,21 +932,42 @@ clearly not necessary. |
886 | 932 | -- @ |
887 | 933 | -- runArraysHetOfThen |
888 | 934 | -- :: (forall s1 s2. |
889 | | --- ((forall x. MutableArray s1 x -> Compose (ST s2) q (r x)) -> t (MutableArray s1) -> Compose (ST s2) q (u r))) |
| 935 | +-- ( (forall x. MutableArray s1 x -> Compose (ST s2) q (r x)) |
| 936 | +-- -> t (mut s1) -> Compose (ST s2) q u)) |
890 | 937 | -- -- ^ A rank-2 traversal |
891 | 938 | -- -> (forall x. Array x -> q (r x)) |
892 | 939 | -- -- ^ A function to traverse over the container of 'Array's |
893 | | --- -> (forall s. ST s (t (MutableArray s))) |
| 940 | +-- -> (forall s. ST s (t (mut s))) |
894 | 941 | -- -- ^ An 'ST' action producing a rank-2 container of 'MutableArray's. |
895 | | --- -> q (u r) |
| 942 | +-- -> q u |
896 | 943 | -- runArraysHetOfThen trav post m = getConst $ |
897 | 944 | -- runArraysHetOf (\f -> coerce . getCompose . trav (Compose . fmap post . f)) m |
898 | 945 | -- @ |
899 | 946 | runArraysHetOf |
900 | 947 | :: (forall s1 s2. |
901 | | - ((forall x. MutableArray s1 x -> ST s2 (Array x)) -> t (MutableArray s1) -> ST s2 (u Array))) |
| 948 | + ((forall x. MutableArray s1 x -> ST s2 (Array x)) -> t (mut s1) -> ST s2 u)) |
902 | 949 | -- ^ A rank-2 traversal |
903 | | - -> (forall s. ST s (t (MutableArray s))) |
| 950 | + -> (forall s. ST s (t (mut s))) |
904 | 951 | -- ^ An 'ST' action producing a rank-2 container of 'MutableArray's. |
905 | | - -> u Array |
| 952 | + -> u |
906 | 953 | runArraysHetOf trav m = runST $ m >>= trav unsafeFreezeArray |
| 954 | + |
| 955 | +{- |
| 956 | +This alternative version is arguably prettier, but it's not compatible |
| 957 | +with the traversal functions from rank2types or compdata for the same reason |
| 958 | +that the prettier version of 'runArraysOf' isn't compatible with 'traverse'. |
| 959 | +It can be implemented with a bit of ugliness. |
| 960 | +
|
| 961 | +runArraysHetOf' |
| 962 | + :: (forall s1 s2. |
| 963 | + ((forall x. MutableArray s1 x -> ST s2 (Array x)) -> t s1 -> ST s2 u)) |
| 964 | + -- ^ A rank-2 traversal |
| 965 | + -> (forall s. ST s (t s)) |
| 966 | + -- ^ An 'ST' action producing a rank-2 container of 'MutableArray's. |
| 967 | + -> u |
| 968 | +runArraysHetOf' trav m = runArraysHetOf (\f -> trav f . unBaz) (coerce m) |
| 969 | +
|
| 970 | +type family Gross ms where |
| 971 | + Gross (_ s) = s |
| 972 | +newtype Baz t ms = Baz {unBaz :: t (Gross ms)} |
| 973 | +-} |
0 commit comments