Friday, November 3, 2017

Continuations and type systems

How do we get a programming language which combines all the type features we have seen? Linear types, dependent types, subtypes, continuations rather than functions, jumbo types,and so on?

First we must recall the purpose of a type system: at least at a low level, types are calling conventions. The type of a function, module, etc. is its interface, its API. A function may have multiple types and hence multiple calling conventions; but at runtime presumably only a few of these will be used. For efficiency reasons it makes sense to have only one true type and construct all the other types as wrappers, but this is by no means necessary.

When we have a C type, for example int main(int argc, char **argv), this is actually a very strong type; the C standard and implementation specify many details of memory layout, stack usage, etc. that are assumed to hold. The interface with the kernel is a cleaner type; execve simply loads the proper memory layout and jumps to the starting address. To represent this API we must use continuations instead of functions, as they never return a value. After looking at IL we might denote this as the type ¬kernel. Expanding the type kernel out it might look more like ¬(stack pointer -> {argc, argv, env}), at least on Linux. Continuations map nicely to the power of assembly language, where jumping to an unknown code segment gives up all control. If a C 'function' calls exit() it will never return. In IL, there is also a 0 type, the type of 'enterable locations'. From the rules the only values of type 0 look like (λ x.F) T, where F is another value of type 0, and the only values of type ¬T look like λ x.(E1 E2). It's not clear what this is useful for.

At a high level, though, the idea of contracts remains. We have properties of our programs that we wish to remain true, like memory safety. Linear types are one method of enforcing these; we reject programs that don't use resources linearly. Dependent types are another form of contract, allowing us to write pre- and post- conditions on values.

Following the IL paper, and its related cousin TACC, we can define types of continuations - roughly, the type of a continuation is the argument layout it expects to have. We extend our data types to the Jumbo Connectives of arbitrary records and sum types (the Pi type can be ignored, continuations are more powerful than functions as an intermediate language). And also we have to generalize negation to delimited continuations, following Zeilberger and a more recent paper. We can implement these on LLVM in two ways: first, by translating them away into standard function calls, and second by using a special calling convention, jump-with-argument.

How do we combine linear types with continuations? Following this paper we might introduce multiple types of continuations, e.g. the paper's lowered negation ↓¬ and boxed negation . Then linear function types translate as !¬(τ1 & ↓¬τ2) while normal function types translate as !¬(τ1 & !¬τ2). But this hides the Rig (semiring) structure of the context. Maybe instead we could use a numbered continuation, , where k is from our Rig. Then adding dependent types is easy, and subtypes are just polarizing all the information flow. Simple!

Papers by Appel (1, 2 , 3, 4) describe a closure strategy analysis, wherein known functions are compiled using registers but unknown functions use the heap. They do not use the stack because it inefficiently holds on to values even after they are not being used; deallocation would require making holes in the stack. But of course nothing in life is free; as soon as one gives up the stack, one needs it again for interfacing with other (C) code.

Thursday, November 2, 2017

Incremental build farms

Build farms are expensive to setup and maintain. Or are they? Amazon gives away free instance-years, and services such as Travis and Appveyor build open-source projects for free. Can these services be used to bootstrap a Linux distribution?

The Nix answer to these questions is to use a binary cache - a server in the middle that stores cryptographically-signed binaries. It can start as simple as a single node, with a Nix store, then expand to use Amazon S3 or another large-scale filesystem.
But why start there? We could instead start with a distributed filesystem, like Bittorrent's DHT or IPFS. Then sharing files is as easy as sharing the root location hash, and all we need is a simple GitHub Pages site that gives out the root hash for everyone to synchronize on.

The issue of trust still remains though. Who builds what? How does the hash get calculated? Is there a cryptocoin (filecoin)?

Another approach starts from a single moderately-powerful system; Gentoo is a source-based Linux distribution that can usually be compiled and installed overnight. Speeding up Gentoo (re)compilations is a worthy goal. But then how do we go from one system to a network of systems sharing binaries? Gentoo (portage) actually supports binary packages, but in practice this seems like it is never used publicly; instead it has a centralized system of mirrors for distfiles. This is probably due to culture and limitations in compatibility of binary packages; it seems like Gentoo is usually set up to compile packages for the specific CPU it's running on, as a side-effect of too many USE flags ("Gentoo is for ricers"). But doing some sane defaults like Arch shouldn't be too hard, and proper dependency management should alleviate all the compatibility / configurability concerns.

Tuesday, October 31, 2017

Continuous delivery and distribution

Software development suffers from lag; every change must be made, then compiled, then tested in few dozen ways, before the next change can be made. The faster this process completes, the faster development can move forward. Non-incremental development, where changes are made without testing or compiling, leads to slower development times overall due to the numerous regressions that are introduced and have to be tracked down later.

Nix encourages incremental development by making it easy to download and use software; it creates a uniform interface for adding libraries and other dependencies, in the .nix file. But it also slows down development because the build process itself is non-incremental; it has to pull down every source file every single build which adds significant overhead (1.5 minutes for a simple Java app). Part of this is necessary overhead; in a cluster, a build file has to be replicated among the machines. And dependencies have to be expire every so often to ensure they don't get stale. Nix's insistence on knowing the hash of every source dependency before it's fetched means that you can't implement automatic updates without updating your Nix files automatically. But the whole point of Nix is to be a human-readable and human-editable description of your configuration; updating them automatically makes them just another intermediate file format.

A cardinal rule of source control is to never check generated files into the repository; Nixpkgs has been violating this rule, with the consequent ballooning of repository size as a result. Hence the need to start over with a newer, cleaner distro - solve both the incremental build problem, via a new package manager and build tool, and the repository problem, via a more principled approach to dependency management.

The place to start is a small prototype to prove that it works; I only program in Haskell these days. For monitoring file dependencies we can use hs-watchman, although it needs to be updated for 2017. For logging we can use GZipped JSON as the file format and the pipes-zlib library. For process tracing we can re-use the command infrastructure from Shake.

Type system features

Subtyping and open data types

One interesting feature of type systems is extensibility; we have the typical object-oriented inheritance, but this is actually too narrow for a proper type system. We instead have the notion of an extended conversion, a function from -> to. Useful examples start from integer conversions, but the most interesting example is adding more constructors to a datatype; A | B is a subtype of A | B | C. We can also do it with product types, by removing elements; (A,B,C) is a subtype of (A,B) is a subtype of A. It makes more sense with labels and records though; {foo: A, bar: B} is a subtype of {foo: A}. This is the closest it gets to inheritance in a type system. Implementations are few, unfortunately; it's primarily Stephen Dolan's work on BRICK (blog archived) and MLSub. BRICK is notable for supporting equirecursive and regular types, so that records do not need a newtype to destructure or bind, similar to dynamically typed languages such as Python, and for compiling to LLVM (no GC, unfortunately). MLSub is just a boring extension with lots of proofs of correctness in the thesis and the coined terms of "biunification" and "bisubstitution". He basically tries to convince you that his subtyping algorithm is almost as simple to implement as Hindley-Milner; which honestly I agree with.

The most interesting part of subtyping is that it explicitly exposes the idea of polarization or duality; for example a function arrow (A -> B) is contravariant in A and covariant in B. When we type check we can divide the type variables into two classes: the pos-variables (lower bounds) and the neg-variables (upper bounds). In fact we usually need both; for mutable variables, arrays, etc. we need both in and out parameters (what can be stored vs what can be retrieved). For example the parameter type for a function that accepts a mutable array of Ints and only reads from it will be MutArray[store=\bot, retrieve=Int], where the store is a lower bound and retrieve is an upper bound.

Dependent types

Once you have subtyping, what point is there in distinguishing values and types? Letting them be used in place almost interchangeably requires some complicated compilation tricks, and an interactive interpreter, but Idris has shown that it works reasonably well in a practical programming language. But dependent types can be extended further.

Insanely dependent types let you define dependent pairs (Σ A (B a), Idris term a ** B a) using a dependent product (Π, (a : A) -> B a). There's some trickery involved in the implementation, see the source for details. The short answer is you need extra scope to reference all the values, and a careful evaluation strategy to avoid infinite loops. The related idea of circular type signatures allows indexing datatypes by values of the same datatype; for example a matrix indexed by a vector, while defining a vector as a single-dimension matrix of length n. (Their notion of matrix is n-dimensional so really it's a tensor). Also interesting from that reddit thread is the idea of making values (terms) a subtype/instance of their type, so for example 1 is a subtype of Integer.

Richard Eisenberg is working on adding them to Haskell, although only the normal form of dependent types.

Linear Types

We could borrow type constructors from Girard's logic: ! and ?, respectively weakening a type to be usable more than once and to not be used at all. There remains the linear arrow ⊸. The usual intuitionistic arrow A -> B then desugars as !A ⊸ B. But this cannot be the final answer, because in IL we decompose A -> B as ¬(A & ¬B); functions do not really exist. Following this paper it seems like it makes more sense to just add multiplicities to the constructors, 0 1 and ω for erasable, linear, and ordinary types respectively. A note here mentions extending ω to having multiple types.

We could also use monads or their more general cousins of Arrows, but why?

Tweag says they're adding them to GHC. although it's hard to say.

Higher-order Types

This paper seems interesting and useful, shouldn't be too hard to add.

Extended type classes

Haskell's type classes have been wildly successful; from an implementation perspective, they amount to adding a constraint-solving engine that discovers and writes in new terms. But I think type classes should be able to add new type information too; unfortunately my efforts in that direction have been unsuccessful so far. It seems like a simple idea though; the set of type class instances in scope is fixed, so if only one instance unifies with the types in scope then it must be the instance to use (otherwise the term must be resolved elsewhere). This is to be distinguished from GHC's current implementation, which has a more restrictive condition requiring matching instead of unification, where matching is defined to add no new information. The question is how to propagate the information from unification in the constraint solver back to the type checker at large; I don't really know enough GHC internals to diagnose the errors in the test suite I ran into.

Type-directed name resolution

One of the most annoying parts of Haskell is that record constructor names must be globally unique since they count as functions. This has been worked on here, but the problem also shows up with type classes (particularly RebindableSyntax) and just in general with similarly-named or similarly-performing functions. What would be ideal IMO is if Haskell supported ad-hoc overloading, so you could just use the name as many times as you wanted and the compiler disambiguated by type. This would also get rid of the 'import Data.Map as M, Data.Set as S' boilerplate. But this would require intermingling the renaming and type-checking phases of GHC, so unlikely to happen.

Sunday, October 29, 2017

Scope and closures


In functional programming, values are bound to names. But how are names resolved? The main strategies are lexical scope and dynamic scope. Stallman writes some sections on why Emacs Lisp has dynamic scope (now extended to support lexical scope as well). But of course the two strategies are not mutually incompatible; Vesta supports both dynamic and static scoping, with dynamic as the fallback for static. (PDF page 27)  Dynamic scoping is limited to function blocks with ... in the argument list, but at least it's there. Such a feature would be quite useful in Nixpkgs, where packages are repeated twice, first in the formal parameters and second in the actual dependencies, although a version of that syntax was considered and rejected due to being too slow to execute. Vesta and Nix share much in common though; Vesta's runtool is very similar to Nix's derivation, although Nix uses cryptographic hashes and Vesta uses virtual filesystems.

In more formal terms, we may classify scoping as resolving a name along two dimensions, its semantically enclosing environment (stack frames or dynamic scope), and its syntactically enclosing environment, its AST tree or lexical scope. A filtering function selects from the possible environments and can produce an error or the proper resolution of the name.

Despite the name, dynamic scoping is not incompatible with static typing; a syntactic analysis of which names resolve where is easy, and whatever names remain unresolved can be resolved dynamically.

But the question of how environments/scopes are implemented is not trivial; a paper I read but lost lists many strategies, with choices such as heap-allocated vs stack-allocated, callee-saved vs caller-saved, and ordering of the frame layout. Given the choices, it seems foolish to lock oneself into a particular strategy, at least at a low level.

Saturday, October 28, 2017

A new build system

A survey of build systems, and their advantages:
  • Make - installed everywhere
  • Ninja - portable format
  • Shake - parallelism, unchanging files, monadic dependencies
  • Pluto - dynamic dependencies
  • Tup - fast rebuilds with a filesystem monitor
  • Nix - configuration management, isolated builds
There are no easily found build systems that combine all these advantages. Why not? Probably due to the difficulty of switching projects over to new build systems; most people are happy to let sleeping dogs lie, even if in this case they are really hissing snakes that are still biting and constricting developers.

With no existing build system, the solution is to write a new build system. Relevant xkcd:
https://xkcd.com/927/

But the problem is in fact that there is no standard for build systems, just people (companies) doing whatever they feel like. When a committee comes along, develops USB-C, Unicode, etc., in practice it seems that everyone does end up switching. In software, there's an even stronger standardization process: the best open-source implementation usually ends up becoming the de-facto standard, just because everyone else is too lazy or too stupid to reimplement it. Look at LAMP and its history, or more recently Wordpress.


The closest candidate to a de-facto standard for build systems is Make; it has a large user community. But it's missing all the features of the other systems. Can we extend make? Probably not, look at the source, e.g. the dependencies file and main.c and job.c. It's heavily process-based and GNU-centric, the deferred string processing and loading of commands makes everything really slow, and then of course it's in C, which isn't exactly my language of choice. Also it doesn't support parallelism on Windows (maybe?).  OTOH a new implementation maintaining backwards compatibility with make is definitely possible - for example Mozilla has been using PyMake since 2011. Python has been much more successful as a scripting language than GNU Make's built in scripting language of Guile. And porting even a large project isn't that hard, the Tup guy got halfway there already. The hard part is changing over tooling / automation / infrastructure which assumes the use of Make. But as Tup mentions, make is flawed in the way it's most commonly used (overly recursive, incomplete DAG) and also slow because of how it processes files. And it doesn't use a database, all the cool build systems have a database. So all in all, Make is dead for all standards-making purposes; nobody uses it anymore in their new projects, except GNU. And autoconf can generate config files, so there's no need to replace the whole GNU toolchain entirely. Just replacing automake should be sufficient.

Requirements for a new tool in priority order, from 2007:
  1. Dependency Management (we have many many libraries)
    1. Search Paths 
    2. Variants
  2. Maintainability
  3. Cost of training for day-to-day tasks (40+ developers)
  4. Portability (but Linux is a must)
    1. Windows, MSYS
  5. Speed (A distant fifth; anything would be better than recursive make.)
But of course everyone has their own priorities. The only real priority is avoiding yet another Make DSL.

Friday, August 18, 2017

Metropolis (2001) Review

An excellent example of programming gone wrong, with detailed visuals.

General takeaways:
  • Don't store all your data in a single giant tower blocking out the sun
  • The most interesting details are buried at the lowest level
  • Robots aren't good for much more than handjobs
Specific takeaways: Tima reversed is "Am I T?". The answer is no.

Monday, July 10, 2017

Tools for fools

In project management there's this idea of "fast, good, cheap; pick two":
https://en.wikipedia.org/wiki/Project_management_triangle

But project management triangles like these, for software, are almost always a lie; what works is requirements planning. So I'll list the requirements for a make-like tool:
  • sandboxed / traced model of task execution, capturing dependencies (file I/O, subprocess creation, etc.)
  • database (store) of recorded execution traces for each task
  • unique stable identifiers for tasks, so you can build subsets of them and modify the task graph from the command line (like --touch and make a.o b.o)
  • scheduler, buffering up file changes etc. and deciding which tasks to run when (ideally with the usual enhancements: multicore/multithreaded, perhaps distributed across a build farm)
I think that's as far as I can go here; the requirements are pretty generic. But in a bigger organization like Google or whatever it makes sense to encode the knowledge as software so it can be shared as quickly and easily as possible.

Thursday, June 1, 2017

The Progress UI Anti-Pattern

Lots of software these days displays progress indicators: spinning hourglasses, bouncing balls, whirling windmills, or cataclysm countdowns. It is well-known in the UI world that animated progress bars improve perceived user experience. Holding the software constant, if nothing is displayed during an ongoing task, the user will lose interest in it and begin to wonder if the computer is frozen. Whereas, if a spinner is displayed, the user will stay within the interface and not complain about it. Finally, when the progress bar has pulsating ribbing moving backwards, time appears to move faster.

What is less well-known is the mechanism for this. As usual in neuroscience there is still no conclusive theory of the perception of time, but instead some competing theories and interesting experiments. In this study they suggest that their rats use head behavior as timing; basically, the rats started a dance, and compared the ending of the stimulus with where they were in the dance. In this paper we see that training can create reasonably accurate neural signals corresponding to interval timing tasks, but that the mechanism for these is still unknown. Finally in this paper we observe multiple timing mechanisms in use in humans; widespread counting mechanisms, a comparison system in "the temporal semantic system", and a switch in collation mechanisms between the default-mode network and the the motor network around 2 seconds. The motor network is active for durations in the hundreds of milliseconds, firing at 30-40 Hz, while the default-mode network is active for longer durations (above 2 seconds), firing at 10 Hz.

So, we can confirm the 1-second flow rule; after 1 second (closer to 1.5 according to experiment), the default-mode network gets activated and the user loses engagement with the task.

But the 10 second rule is pretty arbitrary, and the decision to use progress as the indicator even more so. Following Edward Tuft's simple proviso of "maximize the data-ink", we can create guidelines. For an indeterminate progress indicator, only the state is actual data, and the rest of the animation can be removed. A simple state indicator such as color suffices. I have been testing this myself for the past few years using my Static Throbber theme in Firefox. I can report that I now hate seeing the animated throbbers on other browsers; they just look tacky.

So, this works for short indicators, less than ten seconds. But, sometimes we have longer things. For example, while I was writing the last sentence, Firefox froze for 20 seconds, because of some JavaScript in a background tab. Well... that still doesn't count. The window manager can give Firefox in a "non-responding" window decoration, and since progress is indeterminate that's really all that can be done. (And for the case that the window manager freezes... that's why I have it displaying a clock with second-level precision, so as to see if it's still progressing. If the clock stops ticking, it's time for a reboot). Alright. So 20 seconds is too short to start thinking about estimating progress; how long do we have to go? I would guess the 5-minute range; after that, it's long enough to start thinking about switching tasks and doing smallish routines such as preparing meals.

For estimated times, the only useful data is the time to completion, e.g. 24 minutes. But this changes frequently; a better idea for long-style progress indicators is to display the time of completion, e.g. that it will finish at 3:00 PM today. Calendars are better than ETA's because they let us plan in advance and think about all of life's routines as opposed to the task at hand. There's some debate over whether a clock time is shorter. In practice, estimates will be inexact, and you'll need a confidence interval. So far I haven't seen anybody implement progress estimation using anything approaching statistics... It's really the only way to display time; the progress bar is useless, because it only shows how much time the user has sunk in already. Even in an early study on progress bars, the suggestion is made to display a simple time estimate instead of a progress bar.

Technology hijacks people's minds; is watching a progress bar tick really the best use of their time? Optimizing for software satisfaction leads to hidden costs in other areas of life.

Corroborating evidence comes from a study of keyboard vs mouse: the mouse was easy to use and was objectively faster, but, due to its low cerebral engagement, was rated poorly by users. On the other hand, the keyboard required remembering complex shortcuts, which took significant cognitive processing. But this time was ignored in subjective evaluations, resulting in users thinking that the keyboard was faster. User satisfaction would be optimized by keyboards. But user productivity would be optimized by mice. But, only in the short term; more recent studies suggest that, with experience, keyboard shortcuts are faster than the mouse. So the choice is best phrased as an investment decision: spend more time now for a better memory later, or walk away quickly and cut your losses.

Obviously, if the software will take several days to complete, it is pretty much impossible to remain engaged and you should always switch tasks. Similarly if it's below the threshold 0.1 seconds then maintaining flow is paramount. But in between, we enter a much more complex world than a simple progress indicator. Questions like: How should users spend their attention? Is frequent task-switching a good idea?

Model for why spinners reduce attention span:
  • the animated spinning engages the user's attention
  • the default-mode network is thus less active, fires less often, and so it feels as if not much time has passed
  • the timing from the motor network fires more often to track the on-screen activity

A solution for semi-autonomous vehicles

Today, there are many discussions around self-driving cars and whether they can be trusted to take over from people. Although researchers are working hard to change this question into the reverse ("Why should humans drive when the computers do it so much better?"), there will (probably) always be a need for manual driving; for example, the car would have to be driven by hand to the repair shop when an important sensor breaks.

The problem, of course, is that if the car is doing most of the driving, then humans will be unprepared for the rare occurrence when it does not. The most fanciful depiction of this is probably the Disney movie WALL-E; the humans have retreated to self-driving floating armchairs, and barely even recall how to walk. As shown in that movie, though, the situation is unstable; if the systems stop working, which in WALL-E's case was as simple as a new robot bumping into one of the chairs, humans are adaptable enough to learn complex control systems on-the-fly and take back control.

But, of course, it is possible that humans could be injured or killed in this (re)learning process. For the most part, this has been worked around; e.g. car companies designed safety features such as seatbelts and airbags into their cars. But these are not sufficient; there are still many non-fatal injuries, and unlike with bumper cars, real cars cost a lot of money to fix when they crash into each other (exactly why this is so is up for debate).

So, I present the solution: video games; in particular, car racing games. Supposing that, in an autonomous car, the controls will be disconnected, this frees them up to be used for other purposes. By adding transparent LCD screens to all the windows and utilizing the already-present sound system and computer, a decent video game can be made even better through an immersive in-car experience. Furthermore, since it utilizes the existing car controls and view mechanisms, it ensures that the driver is well-acquainted with what to do in case of manual control.

Existing car games are already headed in this direction; dedicated vehicle gamers have a custom steering wheel, electronic pedals, and even a stick shift, for those who like manuals.

It seems simple enough; why drive your boring, slow routine route when instead you could be racing down the freeway, narrowly avoiding the flaming wrecks of other cars and dodging the oncoming fire of a police helicopter? Considering the level of skill required by these video games, transferring to manual control will be a piece of cake; for those who have things to do besides play racing games, a few minutes a day would likely be sufficient to maintain the necessary skill level.

As usual, I have no clue to why this hasn't happened; a similar system could easily be built for pilots, who already do very little manual flying. Instead, though, plane crashes continue to happen, primarily due to pilot error, while those pilots who actually do bother to play airplane simulator games, and then go on to use those skills to recover from unexpected events in their day-to-day flying, are lauded as heroes who went above and beyond their job description.

Now, I'm not saying these people don't deserve recognition; most video games include a leaderboard or a high score feature for that very reason. But, when they appear in the newspaper, rather than the video game forums, I think there's a bit of a problem. Video games can and do save lives; let's make them part of the job, rather than the main event.

Data is abstract

Take a hard disk. Print it out on a sheet of paper. It's just a long string of bits. The processor does nothing but shuffle around these bits, using some of the bits to interpret the other bits.

DNA is the same; instructions to build proteins to build cells and build more DNA.

The interesting parts are the concrete details; what kind of machine gets built. And they're not really in there; looking at machine will tell you what kinds of instructions there are, but not how to interpret them.

Teeth Care

(There are highly-abbreviated notes loosely based on "Kiss Your Dentist Goodbye". Read the book or google for clarifying details.)

  • Tooth decay could be called an epidemic 
  • widespread, not really treated
  • almost all adults, even those flossing regularly, eventually get dental disease
  • 1/5 Americans rate dental health as fair to poor
  • 1/4 of young people have gum disease
  • tooth decay 5x more common than asthma in childhood
  • All sorts of diseases have been linked to it: cancer, acid reflux, heart problems, high blood pressure, stroke, insulin instability, infertility, premature birth, pancreatic cancer, bowel problems.
  • Health care systems mostly suck, incentivize expensive treatments instead of prevention - good for research (and researchers), bad for the people involved

Identifying a good dentist

Despite name of the book (implying that you never need see your dentist again), she's a dentist and still recommends you see one occasionally.

A good dentist:
  • prevents problems, avoids drilling; if treatment is necessary, chooses minimally invasive procedure
    • avoids the common error of filling a perfectly sound tooth
    • Identifies fillings when damaged by acidity (tooth around it erodes, comes loose) - can offer array of options, avoids using toxic chemicals such as mercury
    • ideally: screens to help you prevent cavities, does not handle treatments or fillings directly (thus no conflict of interest)
    • compare "drill, fill, cover" vs. leaving it in place and covering w/ porcelain pastes
  • is skeptical of industry claims, communicative, source of info, experienced, knowledgeable, caring, effective, etc.
  • believes in prevention, remineralization
  • "mouth fitness trainer"
  • interviews patients regularly to discover cause of dental damage
  • has special equipment
    • ultrasound depth testing, Inspektor dental light, digital imaging fiber-optic trans-illumination (DIFOTI), DIAGNOdent laser (kavo usa), inspektorpro, x-rays, ...
    • see cavities, weakened areas
    • measure tooth strength/thickness
    • see at regular intervals
    • provides advance warning/praise
  • uses blunt instrument to check for rough spots, not explorer - see article "Should a Dental Explorer be Used to Probe Suspected Carious Lesions?"
    • explorer - pushing sharp point into weakened area makes it harder for tooth to repair itself
    • sticky spots not reliable diagnosis for cavity - only 24/100 w/ cavity in one study
  • hunts for new dental ideas or methods to stop dental problems
    • at least: dental school, journals, and education programs
    • attends lectures on preventative care
    • interacts with other dental professionals
    • reads (some of) Dentistry Journal, J of ADA/Calif DA, J of D Education/Research, General/Pediatric Dentistry, Caries Research, Archives of Oral Biology, FEMS Microbio Reviews,  J of Clinical Microbio, Infectious Immunology, J of Clinical Pediatric Dentistry, oral health and preventative dentistry, Micro-organisms of human mouth, J of Clinical Periodontology, A J Epidemiology, J of Endodontics, Annals of Pharmacotherapy, 
  • Cleanings make bacteria attach more easily, but also remove a significant amount of them. Unclear if they're beneficial, if you have a good home care routine. (if you don't take care of your teeth then of course they help...) 
old method of fillings - cut grooves into tooth to retain filling
new method - etch, fill pores w/ plastic resin ("sealant")
sealants useful for small cavities, pits, grooves - worry about bacteria though

    Minitutorial on teeth and how they decay

    Structure of tooth is similar to egg - shell corresponds to enamel - immerse in vinegar, shell turns to rubber (but does not vanish; just breaks really easily)
    soft teeth look dark or yellowish, just need time/care to harden/lighten - don't use bleaching agents
    note that fluorescent lights make teeth look yellow regardless

    tooth parts (outside to inside):

    • Plaque - mesh/biofilm of protein strands, many bacteria types, and other substances, fluids, and cells; can have a protective or harmful effect on teeth
    • enamel - continuously changing mesh of watery film and mineral crystals (calcium hydroxyapatite - key elements calcium and phosphate)
      • in presence of fluoride, crystals form faster, structure is calcium fluorapatite - larger, stronger, smoother, more symmetric, and shinier crystals
      • acid dissolves both types, some is normal but too much is bad
      • porous teeth are sensitive, fragile, and temperature-sensitive
      • demineralization/remineralization - shrinkage/growth of crystals -acid demineralizes, saliva remineralizes
      • usually clear, like glass
    • dentin - softer than enamel, very porous, creamy white, tubes
    • pulp - soft living tissue w/ blood supply - odontoblasts
    • gum - seals roots off from rest of mouth, protects roots

    Caries

    • bad bacteria transferred by contact w/ infected saliva on food, utensils, hands, etc.
      • There are ~700 species of bacteria, mostly competing for resources in the mouth. Their functions are still an active research area, but it's clear that some are worse than others in terms of cavities, and you want to get rid of those, e.g. Streptococcus mutans and some lactobacilli. Yeast is another possibility.
    • bacteria attach to a hard, non-shedding tooth surface
    • eating - bacteria process some of the sugar and produce acid
    • acid eats away at the nearby portions of tooth, weakening it and eventually causing cavities
    • bacteria cause gum disease too, attach near gum/tooth line instead of on tooth

    Signs that you have a problem (from bad to worse):

    • gingivities - mild inflammation of gum, 1/7 adults
    • enamel on outside surface of back molar teeth chips away (where teeth bend and flex)
    • back teeth are sensitive
    • bleedings gums
    • periodontis - severe inflammation of gums
    • periodontal pockets form
    • fillings fail and need replacement
    • gums need extra cleaning
    • gum recession, exposed roots showing cementoenamel junction
    • root-canals and crowns
    • extractions, implants, bridges, dentures

    Ways to prevent:

    (all of these happen naturally, but you can help the process by being conscious of it and using various products)
    1. Control which bacteria you have - don't share saliva, remove infected clumps of bad species, eat clean food
    2. Prevent bacteria from attaching to teeth - brushing, flossing (questionable), chew gum, protective sealants, fillings
    3. Kill the bacteria - xylitol, antiseptic rinses, less mouth sugar/food, immune system
    4. Protect teeth - fluoride, pH, minerals, saliva flow (gum, ...), protective rinses

     Methods

    Fluoride

    • strengthens teeth. benefits mostly when in direct contact w/ outside of tooth, for as long as possible
    • good for teeth in moderation (toothpaste!)
      • extreme amounts can cause fluorosis in anyone
      • fluoride before age 3 can poison ameloblasts (teeth cells) and cause even more problems
      • breast milk has low fluoride, infant formula might have high fluoride - bad
    • types:
      • sodium fluoride - well studied, pretty safe, more expensive than other types
      • stannous fluoride - reduces gum inflammation, but brown/black staining of teeth
      • silicon fluorides - not well studied (commonly found in fluoridated water)
    • fluoride sources - foods, sodas, beer, infant formula, powdered iced tea, ...
    • Fluoride rinse - last thing before bed, first thing in morning; spit at least twice to remove extra stuff
      • recommendation: dilute ACT 0.05% fluoride, with no alcohol
      • very little is needed, just enough to coat the tooth surfaces (a few drops)
      • rinsing more often w/ fluoride gives better results
    • Topical high-concentration rinse doesn't do much for healthy teeth.
    • Fluoride gels/foams only effective when applied for >4 minutes on disease-laden teeth
    • fluoride varnish - recent option for low-dose fluoride, numerous studies touting effectiveness
    • Fluoridated water... but:
      • tends to use less-studied (cheap) fluorides, which might have adverse health effects (e.g. heavy metal poisoning)
      • can't control - it's better to filter your tap water and buy your own (sodium) fluoride

      Behavioral Saliva /  PH Control

      • Probably the most effective, after fluoride.
      • Measure saliva pH (litmus, special probe), also amount (saliva buffer kit)
        • To take mouth acidity reading: 
        1.  Spit saliva into spoon
        2.  Test w/ pH detector
        •  do when you wake up (base pH, should be neutral or alkaline, close to 7, <6 is worrying), then 10-minute intervals over course of day to see how drinks/foods change the pH - typically return to normal takes ~30min, quicker is better
      • Know pH of liquids you commonly drink (e.g. the water)
      • Saliva contains minerals for rebuilding teeth (remineralization), lubricates teeth so they don't grind while eating; normal saliva contains all the needed minerals, specifically rinsing w/ minerals is generally unnecessary
      • dry mouth is related to dry esophagus, leaving it open to bacteria or fungi which could give symptoms of acid reflux
      • healthy saliva is neutral or alkaline; should return acidity to safe levels ~30 minutes after eating
      • saliva flow increases at mealtimes and reduces during sleep, varies according to person and situation
      • stress thickens saliva and make your mouth dryer - go to sauna/beach, practice meditation/relaxation, frequent exercise

      Good foods:
      • potatoes, asparagus, broccoli
      • fresh veggies/fruits, veggie juices
      • herbs
      • vitamins/minerals, and waters with them
      • whole grains
      • bananas, almonds, fresh apples, pineapple
      • milk, cheese, other dairy products
      • tea, coffee
      • chocolate, licorice, pure cranberry juice - intense sweeteners
      • alkaline soups/broths/water
      Non-food stuff:
      • propolis (bee product)
      • chewing gum
      • plant fibers/leaves - even chewing sugarcane is good
      Bad stuff:
      • acidic food
      • acidic drinks (worst to OK)
        • frozen fruit juice
        • lemon juice, lemonade
        • citric, apple, and grape juices - even diluted they still cause problems
        • soda, sports drinks, coffee, beer
      • sugar/carbohydrates (if bacteria present)
        • Halloween candy - best to eat it all at once (unless you're diabetic)
      • corrosive, abrasive, or whitening oral care products 
        • includes Listerine, don't leave it in your mouth
        • bleaching - horrible - damages enamel - sensitive teeth, inflamed gums, gum recession, etc.
      • sugarless products (diet soda, sugarless cookies)
        • sorbitol causes gastric problems and feeds the bacteria too
      • gastric acid
      • grinding teeth stresses them and causes problems

      Ellie's 3-Rinse Care Procedure

      (2x / day, once before sleeping and once ~12 hours later)
      1. Prerinse with pH-balanced/stabilized unflavored chlorine dioxide rinse (Closys/Retardex)
        • salt water probably would work too
        • baking soda,peroxide - sensitize gum, don't work
      2. Brush teeth, in particular gum-tooth edge, all the way around the mouth
        • Use toothbrush - head <1 inch in length, easily-grasped handle, soft/giving bristles w/ round ends, should be able to reach upper outside and lower inside easily
        • Toothpaste - old-fashioned, boring, low-chemical, low-tech, no abrasives, few additives, no whitening, "gentle", sodium fluoride, ADA seal
          • dicalcium phosphate dehydrate- abrasive 
          • recommend Crest Regular Cavity Protection
        • Brush bleeding gums, heals in a few days if bacteria are removed
        • use warm water/prerinse + soft cloth instead of toothbrush if sensitive
      3. Disinfect toothbrush (1x/day)
        • Swish bristles in 0.5oz undiluted antiseptic/antibacterial rinse (Listerine) for 30 seconds
        • Rinse off w/ tap water
        • Alternatively: use UV box
      4. Store toothbrush w/ head upright, in cup, allowing bristles to dry completely before using again
        • ensure head doesn't touch other brushes to avoid contamination
      5. Use antiseptic rinse (listerine), swishing around everywhere in the mouth
        • should have ADA seal, good taste, no whitening/abrasives - can dilute if needed
        • Spit out, don't rinse
      6. After antiseptic, immediately use protective anticavity rinse, esp. before sleeping
        • 0.05% sodium fluoride ACT
        • goal is to rebuild teeth
        • don't wash off afterwards, keep on as long as possible
        • swishing toothpaste around your mouth w/ a small amount of water can be used instead of a rinse, but does require spitting it out again
      The toothbrush breaks up plaque films and rinsing afterwards mechanically removes most bacteria. Flossing breaks up plaque as well, but it's not particularly effective (it's mostly good for removing food particles). However, the toothbrush is inadequate for periodontal pockets, only goes ~4mm deep. A Waterpik works but breaks far too easily; the antiseptic mouthwash works pretty well for gum pockets. Also see Periogen, not mentioned by Ellie but seems effective at periodontal restoration / prevention (it has a cult internet following, although it's overpriced). Similarly sodium hexametaphosphate inhibits calculus and loosens extrinsic stains, and in general there are lots of interesting rinse additives to explore.

      Xylitol

      wood sugar, available in specialty stores
      Sciency mumbo-jumbo:
      • antibacterial effects
      • less buildup of plaque for some reason
      • 5g had strong effects, even with a 3 month break
      • 6.5g a day - after 5 wks, reduced; afer 6 months, bacteria gone. for 2 years, plaque reduced significantly - no buildup at all.
      • past 10g/day, no generic effects, but some suggest for calcium absorption
      • xylose + other 8 sugars precursors to some important proteins
      • also reduces ear infections
      • stimulates saliva flow like other foods 
      • 15g naturally produced by body (though not in saliva)
      • 100g/day eaten w/out obvious ill effects

      Ellie's website (Zellie's) sells xylitol wipes, gum, mints, rinses, etc., obvious conflict of interest. She recommends 6-10g/day in 3-5 doses. Buying in bulk from her website, the cost per day ranges from 75 cents (gum) to 48 cents (mints) to 13 cents (granular xylitol). For comparison, the general recommendation is to use 1g of toothpaste and brush twice a day, so your typical $3 232g tube of Crest Regular Cavity Protection toothpaste is only 2.5 cents a day, and 20ml of $5/L Listerine mouthwash once a day is 10 cents a day.

      Thoughts on Google I/O 2016 Keynote

      So, I watched the Google I/O 2016 Keynote. As usual, my thoughts are different from other people, so it's worth writing them down.
      • Music: those were some cool people in the beginning. But Google can't find their music, and they have no credits in the schedule. What gives?
      • Intro movie: reasonable, I guess it was a spin on the play store. Dubstep's still not my thing.
      • Knowledge Graph: still not useful enough to disambiguate "simon haskell" into "simon peyton jones" and "simon marlow". And considering the reasons for the removal of the 'phonebook' operator, I'm not sure it ever will.
      • Machine learning: it's (mostly) open source, no real objections here
      • Buying from movie theaters: Is this still a thing, in 2016? What's wrong with Netflix? I assume Netflix can give much better movie recommendations than Google (although it would be nice to have competition), and unlike Google there aren't any difficulties with availability.
      • Ordering food: the ordering part is ok, but I'd rather have it show up as a persistent notification than as a text bubble
      • Google Home: The majority of examples there should be automated; telling a speaker to turn on the lights every morning is tedious. The flight planning stuff and sensor control seems like it would work better with a touchscreen.
      • Allo: the intelligent completion should be in the system-wide keyboard, rather than the messaging app. Too late now I guess.
      • Duo: same here, improve the platform rather than the app
      • Watch: sounds like watches are the new phones. but do they make calls?

      Tuesday, February 28, 2017

      An immediate interpretation of probability

      Given that many interpretations of probability have been considered and rejected*, it is not clear that a simple interpretation of probability exists. For example, the rhetorical flourishes of the simple interjection “Fat chance!” are unlikely to be captured in a rational* discussion. Furthermore, even if such an interpretation did exist, it is not at all obvious that a paper should be written about it. For example, a study of monkeys, who share 93% of DNA with humans*, shows that they do not use writing in the wild. Monkeys are nonetheless able to learn simple mathematics* and English*. We thus defer discussion of the subject by treating the paper as a preliminary mere-exposure experiment*; it does not need to be approved in advance due to essays falling under normal educational practices*.
      Our interpretation may be termed immediacy. Consider the statements “Alex is wearing a red sweater” and “Alex is wearing a blue sweater”, and assume that the former was observed yesterday but the latter is present today. Qualitatively, these perceptions are different. We can interact with the blue sweater, for example by splashing paint on it; we term this “near”. In contrast, the red sweater exists only in our mind; we may have recollected incorrectly*, for example, if Alice was wearing a red sweater but swapped desks with Alex; we term this “far”. The far reality is mentally constructed, with no direct perceptual connection.
      Far reality is fragile. Consider a child with no experience of elevators, taking their first ride alone. The door closes on their parents, a number changes, the elevator beeps, and when the door opens again a new space is presented, with complete strangers and no parents in sight. Confusion results, only resolved by introducing a notion of “floor” and its corresponding sensations and notifications of vertical movement.
      A reasonable reaction is to ask is how to avoid the possibility of being confused. Unfortunately, we run into the problem of incompleteness*, and in particular the problem of other humans*. Although a problem may not necessarily have a solution*, it may have an approximation; furthermore, we can categorize the techniques used to approximate the solutions, in this paper termed “mental constructions".
      What are mental constructions? Again, an example: “The visual system, although highly complex*, in its most basic elements appears to function as a recording and processing device*.” The previous sentence is simply a reflection of the dominance of the computational paradigm*. A better model than simple computation is the act of writing itself, wherein letters are sequentially produced on a page by some complex process with memory*. We of course lose aspects of humanity when we attempt to describe it in writing, but it is futile to expect to solve these problems via more writing*, so we stop there. Thus, for our purposes, mental constructions are recurrent processes that result in the production of writing. Finally, then, we arrive at our interpretation of probability; given several possible processes with which to produce writing, select one (for this paper, a randomized variant of the autofocus system*).
      One particular strength of our interpretation is its similarity to the etymology of probability*. The word probability originates from the Proto-Indo-European root *pro-bʰwo-, meaning “‎to in front”. This came to be associated with the Latin probus, meaning good, noble, and virtuous. After morality became a common good*, there were then techniques of probo to test and certify what was good. Hypothetically this then became the Latin probābilis, meaning provable, credible, and finally probability as a measure of provability. In our case, provability is determined by a measure of verbosity; in particular, we measure proof as an estimate of the amount of processing done, via an analysis of the amount of references and obscure concepts used.
      Another strength is its self-referential nature, more precisely homiconicity*; by writing a paper interpreting probability using a probabilistic paper-writing process, we achieve a similar representation of both paper and probability. Our interpretation thus covers a vast conceptual space in a small amount of time by utilizing foundational tools repeatedly, particularly the Axiom of Choice*. Although this might appear to be a weakness, in that the two are mutually recursive and thus could lead to an infinite regress, there are well-established base cases of an empty paper and not writing a paper. Furthermore, the homoiconic property ensures that the presented concepts can be reproduced by reproducing the paper.
      A final strength of our interpretation is its synthesis of concepts present in frequentist and Bayesian probability. By its emphasis on writing, we mimic the emphasis on enumeration of possibilities found in frequentism. However, whereas a frequentist approach might focus on the space of possibilities, e.g. H and T in a coin flip, we fix the choice of possibility space (a sequence of English characters) and instead focus on the choice of process. Furthermore, we leave the general method of choice purposely ambiguous, thus implicitly introducing subjectivist notions of probability. But unlike Bayesian probability we successfully avoid the need to enumerate all possible choices of process, by introducing a stopping procedure (detecting the presence of self-reference). Our interpretation of probability is thus strictly constructionist* and does not introduce problems of computability or practicality.
      Our interpretation does have a flaw, though, in that it does not guarantee optimality in any sense. For example, it does not preclude the possibility of accepting a Dutch book. Although it heavily emphasizes writing, references, and consideration of the means of production*, it addresses the possibility of an unconsidered approach only reactively. Furthermore, it does not necessarily produce agreement among different individuals*; they may make different choices in process. We submit three responses: first, that optimality is a theoretical concern* of no particular relevance to applied mathematics*; second, that our approach is optimal in terms of a suitably defined global measure of cognitive costs*; and third, that one can always postulate an independent third party that would use the process of considering and synthesizing all other processes*, but such a third party does not necessarily exist in practice.
      Although any experiment is subject to uncertainty, we have attempted to minimize it through the use of a controlled environment and hypothetical situations. The most pressing concern is that the experiment could fail to produce any measurable effect. Particularly, its focus on references and concepts makes it conceptually difficult to understand, and thus it may be rejected altogether. Furthermore, the use of randomized concepts may make transitions in the paper appear stilted or disjointed. Time constraints forced the summarization, omission, and consequent misunderstanding of many concepts that require an interpreting paper in their own right. Nonetheless we look forward to seeing the results.