@@ -56,7 +56,6 @@ import GHC.Types (Int (..))
56
56
import NoThunks.Class (NoThunks )
57
57
import Prettyprinter (viaShow )
58
58
59
- -- See Note [Pattern matching on built-in types].
60
59
-- TODO: should we have the commonest built-in functions at the front to have more compact encoding?
61
60
-- | Default built-in functions.
62
61
--
@@ -111,7 +110,7 @@ data DefaultFun
111
110
| TailList
112
111
| NullList
113
112
-- Data
114
- -- See Note [Pattern matching on built-in types].
113
+ -- See Note [Legacy pattern matching on built-in types].
115
114
-- It is convenient to have a "choosing" function for a data type that has more than two
116
115
-- constructors to get pattern matching over it and we may end up having multiple such data
117
116
-- types, hence we include the name of the data type as a suffix.
@@ -941,82 +940,10 @@ goes without saying that this is not supposed to be done.
941
940
942
941
So overall one needs to be very careful when defining built-in functions that have explicit
943
942
'Opaque' and 'SomeConstant' arguments. Expressiveness doesn't come for free.
944
-
945
- Read Note [Pattern matching on built-in types] next.
946
- -}
947
-
948
- {- Note [Pattern matching on built-in types]
949
- Pattern matching over an enumeration built-in type ('Void', 'Unit', 'Bool' etc) is trivially
950
- implementable, see the 'IfThenElse' example in Note [How to add a built-in function: simple cases].
951
- Not so much for algebraic data types with at least one constructor carrying some kind of content.
952
- For example the @(:)@ constructor of @[a]@ has two arguments (an @a@ and a @[a]@) and all
953
- constructors of 'Data' carry something (e.g. 'I' carries an 'Integer' and 'Constr' carries an
954
- @Integer@ and a @[Data]@).
955
-
956
- In Haskell we'd represent the pattern matching function for lists as follows:
957
-
958
- caseList f z xs0 = case xs0 of
959
- [] -> z
960
- x:xs -> f x xs
961
-
962
- but in the denotation of a built-in function all those @f@, @z@ and @xs0@ are of type @val@, i.e.
963
- the type of values that the given evaluator uses (e.g. 'CkValue' for the CK machine or 'CekValue'
964
- for the CEK machine) and we don't know to apply one @val@ to another. We could try to constrain
965
- @val@ to implement some kind of "apply" function or try to somehow "parse" it into Haskell so that
966
- it becomes @val -> val@, but even if there was a way of doing either thing, it would be very
967
- complex and, more importantly, it's just not the job of the builtins machinery to evaluate such
968
- applications, it's what the actual evaluator is supposed to do.
969
-
970
- Hence we employ a very simple strategy: whenever we want to return an iterated application of a
971
- @val@ to a bunch of @val@s from a built-in function, we just construct it as is without trying to
972
- evaluate it and let the evaluator perform all the necessary reductions.
973
-
974
- So if you want to return an application from a built-in function, you need to use 'HeadSpine' at the
975
- type level and 'headSpine' at the term level, where the latter has the following signature:
976
-
977
- headSpineOpaque :: Opaque val asToB -> [val] -> Opaque (MonoHeadSpine val) b
978
-
979
- 'headSpine' takes the head of the application, i.e. a function from @a0@, @a1@ ... @an@ to @b@, and
980
- applies it to a list of values of respective types, returning a `b`. Whether types match or not is
981
- not checked, since that would be hard and largely pointless, so don't make mistakes.
982
-
983
- Back to the pattern matcher for lists:
984
-
985
- caseList f z xs0 = case xs0 of
986
- [] -> z
987
- x:xs -> f x xs
988
-
989
- Here's how we can define it as a built-in function using 'headSpine':
990
-
991
- toBuiltinMeaning _ver CaseList =
992
- let caseListDenotation
993
- :: Opaque val b
994
- -> Opaque val (a -> [a] -> b)
995
- -> SomeConstant uni [a]
996
- -> BuiltinResult (Opaque (MonoHeadSpine val) b)
997
- caseListDenotation z f (SomeConstant (Some (ValueOf uniListA xs0))) = do
998
- case uniListA of
999
- DefaultUniList uniA -> pure $ case xs0 of
1000
- [] -> headSpineOpaque z [] -- [1]
1001
- x : xs -> headSpineOpaque f [fromValueOf uniA x, fromValueOf uniListA xs] -- [2]
1002
- _ ->
1003
- throwError $ structuralUnliftingError "Expected a list but got something else"
1004
- {- # INLINE caseListDenotation #-}
1005
- in makeBuiltinMeaning
1006
- caseListDenotation
1007
- <costingFunction>
1008
-
1009
- All the unlifting logic is the same as with, say, 'NullList' from
1010
- Note [How to add a built-in function: complicated cases], the only things that are different are [1]
1011
- and [2]. In [2] we have an iterated application of the given function @f@ to the head of the list
1012
- @x@ (lifted from a constant value to @val@ via 'fromValueOf') and the tail of the list @xs@ (lifted
1013
- to @val@ the same way). In [1] we return the given @z@, but since we need to return a 'HeadSpine'
1014
- (required by [2]), we have to use 'headSpine' just like in [2] except with an empty spine, since @z@
1015
- isn't applied to anything.
1016
943
-}
1017
944
1018
945
{- Note [Representable built-in functions over polymorphic built-in types]
1019
- In Note [Pattern matching on built-in types] we discussed how general higher-order polymorphic
946
+ Note [Legacy pattern matching on built-in types] discusses how general higher-order polymorphic
1020
947
built-in functions are troubling, but polymorphic built-in functions can be troubling even in
1021
948
the first-order case. In a Plutus program we always pair constants of built-in types with their
1022
949
tags from the universe, which means that in order to produce a constant embedded into a program
@@ -1103,17 +1030,6 @@ throw an "operational" evaluation error). Please respect the distinction when ad
1103
1030
functions.
1104
1031
-}
1105
1032
1106
- -- | Take a function and a list of arguments and apply the former to the latter.
1107
- headSpineOpaque :: Opaque val asToB -> [val ] -> Opaque (MonoHeadSpine val ) b
1108
- headSpineOpaque (Opaque f) = Opaque . \ case
1109
- [] -> HeadOnly f
1110
- x0 : xs ->
1111
- -- It's critical to use 'foldr' here, so that deforestation kicks in.
1112
- -- See Note [Definition of foldl'] in "GHC.List" and related Notes around for an explanation
1113
- -- of the trick.
1114
- HeadSpine f $ foldr (\ x2 r x1 -> SpineCons x1 $ r x2) SpineLast xs x0
1115
- {-# INLINE headSpineOpaque #-}
1116
-
1117
1033
instance uni ~ DefaultUni => ToBuiltinMeaning uni DefaultFun where
1118
1034
type CostingPart uni DefaultFun = BuiltinCostModel
1119
1035
@@ -2346,7 +2262,7 @@ instance Flat DefaultFun where
2346
2262
2347
2263
{- Note [Legacy pattern matching on built-in types]
2348
2264
We used to only support direct pattern matching on enumeration types: 'Void', 'Unit', 'Bool'
2349
- etc. This is because it was impossible to return an iterated application from a built-in function .
2265
+ etc. This is because it was impossible to 'Case' on a value of a built-in type .
2350
2266
2351
2267
So e.g. if we wanted to add the following data type:
2352
2268
@@ -2413,5 +2329,5 @@ concerns are omitted for clarity):
2413
2329
which, for example, evaluates to @fMap es@ when @d@ is @Map es@
2414
2330
2415
2331
We decided to handle lists the same way by using @chooseList@ rather than @null@ for consistency,
2416
- before introduction of pattern matching builtins .
2332
+ before introduction of casing on values of built-in types .
2417
2333
-}
0 commit comments