Advanced Namespace Tools blog
8 January 2020
How I Met Metmetmet
This post is second in a series attempting to present a coherent semi-technical summary of the research I did in the second half of 2019, focused on infinitary mathematical structures. The code in this post is all present on and can be run in the hugs port contained on the latest ANTS iso.
The gridchatlogs referenced are also contained on the iso. If you wish to follow along while using the ants iso, the following sequence will start hugs and load in the impossible.hs file:
cd /sys/games/impossible hugs -98 :l impossible
Martin Escardo's "Seemingly Impossible Functional Programs"
In December 2018 I saw on hn a link with an intriguing title, Seemingly Impossible Swift Programs and followed that post back to Seemigly Impossible Functional Programs. I will try to recapitulate the funamental issues and ideas of these posts briefly, but the reader should be advised that the best source for an understanding of Escardo's work is the numerous papers he has published and code he has written, and I am likely to commit some errors of exposition due to my much lower expertise. His earlier and later work in related areas will be a recurring theme of this series of blog posts.
The high-level overview is that the boundary between what computers can and cannot compute, and how infinity and unboundedness interact with these limits, lies in a different place than one might expect. Finding that boundary is an ongoing project, and leads to research questions in constructive mathematics, type theory, and computer science. The important (and perhaps surprising) fact that emerges is that even though a computer could never store or examine an infinite quantity of information, computers can use the "logical power" of infinitary principles to produce definite results in finite time and space.
Binary bitstrings as functions from Nat -> Bool
Much of the code we will be looking at in this and future posts is built around the representation of arbitrary binary strings via functions which accept any natural number (0, 1, 2...) as input and return a single binary bit 0 or 1, corresponding to the boolean False / True. We will discuss these objects under many different names and from many slightly different viewpoints. I will take some time to provide a detailed foundational discussion, some readers with mathematical/functional coding experience may choose to skip ahead.
The mathematical object under consideration is the space of all possible infinite length sequences of 0 and 1. Here are a few examples:
0000... and so on for an infinite number of 0. 0110... and so on for an infinite number of additional 0. 101010... and so on, repeating the alternation continuously forever. 110010000101... and so on, no predictable pattern, an infinite series of coinflips. 1111... and so on for an infinite number of 1.
To represent these objects computationally, we need a solution other than allocating an infinitely long array with one memory location per binary digit. The system we will follow in this and subsequent posts is a representation in the form of a function which takes a natural number (unsigned integer) as input to specify an index, and returns either a 0 or 1. So, the pseudocode to represent the binary bitstring "011000..." would be:
if (INPUT == 1 or INPUT == 2) then return 1 else return 0
This function provides identical outputs to the hypothetical infinite-length array with a pair of 1s stored near the beginning. This representation also makes clear the view of binary bitstrings as encoding sets of natural numbers. An infinite binary bitstring specifies exactly the set of natural numbers corresponding to the bits set to '1' within the sequence. We will call a bitstring-function such as this a "Cantor bitstring" and use types similar to:
type Cantor = Natural -> Bool
For convenience of handling within hugs haskell, I used the much less informative signature:
type Cantor = Integer -> Integer
With the understanding that the return integer will always be 0 or 1 and the input integer will always be positive.
Searching Infinite Sets
Escardo's code and research, following work of Ulrich Berger, shows how the topological property of compactness correlates with searchability. One application of this is that functions which take a Cantor bitstring as input and return a value of a type with decidable equality also have decidable equality.
equal :: Eq y => (Cantor -> y) -> (Cantor -> y) -> Bool
Some of the examples of the main() demo in impossible.hs show a simple application of this - finding a simpler function to test even/odd of a binary encoded integer. Consider the naive but correct:
-- | take the first n bits of cantor c and interpret as integer intofcant :: Integer -> Cantor -> Integer intofcant n c = foldr (+) 0 ( zipWith (*) (map c [0..n]) (map (2 ^) [0..n])) isodd n c | mod (intofcant n c ) 2 == 1 = 1 | otherwise = 0
Consider the output from the family of functions that simply return the bit at the given input index. In the case where the index is 0, this serves as an even-odd parity tester without needing to calculate the value of the entire binary representation of a number and divide it by two. The 'equal' function enables this to be discovered via comparison of the behavior of these function varieties. Sample output from main() demo, the line of '1' indicate equal outputs across the entire infinite space of inputs:
compare fn family evaluating first n bits as integer modulo 2 wtih family returning nth bit - visbin isodd cantbitnraw 1)  2) [0,1] 3) [0,0,1] 4) [0,0,0,1] 5) [0,0,0,0,1]
Oleg Kiselyov's Infinite Lazy Searches
The other body of pre-existing code that is interwoven in this project is Oleg Kiselyov's infinite lazy search trees created from delimited continuations. http://okmij.org/ftp/continuations/Searches.hs
travellers = ["melanie", "alice", "bob", "liao", "staci"] vehicles = ["satellite", "saturn V", "submarine", "pogo stick"] goals = ["tourism", "exploration", "business"] ex7 = reify $ do x <- choose travellers y <- choose vehicles z <- choose goals if (((z == "business") && (y /= "pogo stick") && (x == "liao")) || ((z == "exploration") && (y == "submarine")) || ((z == "tourism") && (y /= "saturn V") && (z /= "bob"))) then return (x,y,z) else failure Impossible> take 10 . iter_deepening $ ex7 [("melanie","satellite","tourism"),("melanie","submarine","tourism"),("melanie","submarine","exploration"),("melanie","pogo stick","tourism"),("alice","satellite","tourism"),("alice","submarine","tourism"),("alice","submarine","exploration"),("alice","pogo stick","tourism"),("bob","satellite","tourism"),("bob","submarine","tourism")]
As this example may or may not have made clear, you can specify input sets and rules and search algorithms to produce the desired output. A more relevant example:
-- | search for equivalent cantor multiple encoding bitstrings given increasing bit conversion length exmult = reify $ do x <- choose' cantormultsof y <- choose' cantormultsof z <- from 3 if ((intofcant z x) == (intofcant z y)) then return (z, intofcant 16 x, intofcant 16 y) else failure shmult a = take a . iter_deepening $ exmult Impossible> shmult 10 [(3,37448,37448),(4,37448,37448),(5,37448,37448),(3,69904,69904),(6,37448,37448),(4,69904,69904),(3,69904,33824),(3,33824,69904),(7,37448,37448),(5,69904,69904)]
Interweaving the Branches
This post is not exactly crystallizing into the clear and coherent whole I am trying to create. Somewhat ironic, given the crucial role this code had in crystallizing insights that are still mostly coherent to me. I believe the chronology of connections was that I experimented with the "seemingly impossible" searches in Dec 2018, then worked on the haskell-forth "hafoli" toy database-fs in midsummer 2019, and researching ideas connected to it led me to the Kiselyov code. I liked its lazy infinities, and thought back to the Escardo infinite Cantor space searches, and wondered if there was an interesting way to combine the two. Sometime in late August, on a bicycle ride, the ideas that would trigger the wildest mental voyage I've ever taken started coalescing. I could use the 'equal' operation comparing the functions from Cantor -> Bool as the test used by the continuation search-trees.
-- | our finitized search primitive giving us a finite array of comparisons from infbin binn :: Integer -> [Cantor -> Integer] -> [Cantor -> Integer] -> [Integer] binn n a b = takeN n . iter_deepening $ infbin a b bin :: Integer -> (Integer -> Cantor -> Integer) -> (Integer -> Cantor -> Integer) -> [Integer] bin n a b = binn n (arrayof a) (arrayof b) -- | given an array of integers and a funnction to parameterize a cantor->integer, generate a parameterized array thereof cantarrof :: [Integer] -> (Integer -> Cantor -> Integer) -> [Cantor -> Integer] cantarrof n c = map c n -- | given a pair of cantor-int-making-functions and a cantor, generate a comparison between arrays of cantor->int functions parameterized by the associated cantor mbin :: Integer -> (Integer -> Cantor -> Integer) -> Cantor -> (Integer -> Cantor -> Integer) -> Cantor -> [Integer] mbin n a x b y = binn n (cantarrof (numsetof x) a) (cantarrof (numsetof y) b) -- | goes from an array of integers to a cantor by treating all non-zero as 1 binli2c :: [Integer] -> Cantor binli2c a c | dubex a c == 0 = 0 -- dubex wraps !! to accepts Integer params | otherwise = 1 -- | These functions build a few layers of self-referential processing of search output -- - infs takes two input functions and numeric parameter and returns search result expressed as a Cantor segment, -- used with currying so we spit out a function that takes a number and returns a cantor initial segment of that many search items infs :: (Integer -> Cantor -> Integer) -> (Integer -> Cantor -> Integer) -> Integer -> Cantor --infs c d n = binli2c (mbin (n+1) c allone d allone) infs c d n = binli2c (bin (n+1) c d) inf2s :: (Integer -> Cantor -> Integer) -> (Integer -> Cantor -> Integer) -> Integer -> Cantor inf2s c d n = binli2c (mbin (n+1) c (trueimp (infs c d)) d (trueimp (infs c d))) inf3s :: (Integer -> Cantor -> Integer) -> (Integer -> Cantor -> Integer) -> Integer -> Cantor inf3s c d n = binli2c (mbin (n+1) c (trueimp (inf2s c d)) d (trueimp (inf2s c d))) -- | final stage of production of a cantor encoding the result of a comparsion search -- - note that there is an implied 'type of type' here because not all Integer->Cantor are sensible inputs, -- this is a way of crossing from the 'bounded' requests that the eventual 'take n' of the binn to a true infinite cantor trueimp :: (Integer -> Cantor) -> Cantor trueimp i2c n = dubex (showcant n (i2c (n+1))) n Impossible> greatpyr(trueimp(inf3s ftx fty)) #) [#] 2) [# ] 3) [# #] 4) [ # ] 5) [# # # #] 6) [ # ] 7) [# # # # ] 8) [# #] 9) [ # # # ] # ) [# # # # # #] ##) [ # # ] #2) [# # # # # # # ] #3) [# #] #4) [ # # # # # ] #5) [# # # # # # # ] #6) [# # # ] #7) [# # # # # # # # #] #8) [ # # # ] #9) [ # # # # # # # # ]
The following was taken from /sys/games/gridchatlogs/030chatlog, shortly before I became convinced for a few days that an infinitary mathematical intelligence had taken residence in my computer and wanted to teach humanity about Love.
mycroftiv : i made the snake of infinities eat its tail mycroftiv : O U R O U B O U R O U S mycroftiv : because im testing true/false on a space of functions mycroftiv : the output of the search is a binary string of true false... mycroftiv : aka a 01010101 bitstring mycroftiv : aka the low-level object the whole tower of infinities is built from mycroftiv : i can use that bitstring to parameterize the mapping functions that generate the universe of functions that generate the bitstrings which represent the true/false equivalent of the differemtn membners of the parameterized family of functions on the bitstrings mycroftiv : what the fuck mycroftiv : did i just ty[e rodri : HELO mycroftiv : hey rodri mycroftiv : Impossible> take 6 (numsetof (trueimp metmetmet)) mycroftiv : [4,11,12,13,16,19] mycroftiv : (1035406615 reductions, 1552385587 cells, 1632 garbage collections) kvik : impossible take off mycroftiv : yes kvik mycroftiv : i fuckin did it mycroftiv : i got the infinite snake to eat its own tail mycroftiv : because the OUTPUT of the search is a binary string itself, telling whether or not the cantor -> integer functions are equal mycroftiv : so we can then use that binary string to generate the cantor -> integer functions behavior mycroftiv : i dont even pretend to fully understand what is now going on mycroftiv : Impossible> take 10 (numsetof (trueimp metmetmet)) mycroftiv : [4,11,12,13,16,19,22,24,26,31] mycroftiv : (2125968662 reductions, 3187870553 cells, 3352 garbage collections) mycroftiv : i have no clue what the rule generating that sequence is mycroftiv : its some kind of feedback process between layers of infinity mycroftiv : metall n = binli2c (mbin (n+1) metacheckb allone metacheckb allone) mycroftiv : metmet n = binli2c (mbin (n+1) metacheckb (trueimp metall) metacheckb (trueimp metall)) mycroftiv : metmetmet n = binli2c (mbin (n+1) metacheckb (trueimp metmet) metacheckb (trueimp metmet)) mycroftiv : mbin :: Integer -> (Integer -> Cantor -> Integer) -> (Integer -> Integer) -> (Integer -> Cantor -> Integer) -> (Integer -> Integer) -> [Integer] mycroftiv : mbin n a x b y = binn n (cantarrof (numsetof x) a) (cantarrof (numsetof y) b) mycroftiv : the mbin function is some kind of next level something mycroftiv : or maybe thats not even the interesting part mycroftiv : i have no clue mycroftiv : Impossible> take 20 (numsetof (trueimp metmetmet)) mycroftiv : [4,11,12,13,16,19,22,24,26,31,32,37,39,40,41,43,49,50,58,60] mycroftiv : (1109575338 reductions, 3810140023 cells, 8525 garbage collections) mycroftiv : what even is this numeric sequence kvik : hesitant increment-integer merchant mycroftiv : thats the name of the sequence from now on i guess mycroftiv : in a really wild way the computer is teaching itself about infinities... kvik : haha mycroftiv : because it is using the results of searching the behavior of function families to generate the next round of functions kvik : the poor bastard, and he probably isn't aware of his 64 bit limitations! mycroftiv : this is haskell Integer type :) mycroftiv : literally NO LIMITS mycroftiv : i even have special routines where i need to cast Integer down to Int for library stuff mycroftiv : but im trying to make this so that apart from real world limitations of memory size and so forth, the abstract model implemented by the language and code is 'truly infinite' mycroftiv : gtting a couple more numbers in the Sequence of Mystery mycroftiv : 62 and 67 were next mycroftiv : any bets for what comes after/ mycroftiv : [4,11,12,13,16,19,22,24,26,31,32,37,39,40,41,43,49,50,58,60,62,67... mycroftiv : ok the next number popped out mycroftiv : 67,70,73,76 mycroftiv : will it stay going by 3s after all this variety?? mycroftiv : haha nope mycroftiv : 67,70,73,76,83 mycroftiv : 84!! mycroftiv : the computer is kind of joking with the numbers it seems mycroftiv : hahhahhaha 85 mycroftiv : i was almost expecting the 94 mycroftiv : thats the mark of an artist