We are increasingly lost in the sea of information that surrounds us.
So many valuable things we discover are being routinely lost. So many contexts that ought to, never do intersect. So many thoughts that could, never come.
Why?
Because data entry, interning and recovery is harder than it could be.
Holotype is a general-purpose mind assistant, that is an attempt to change that.
A balanced, varied approach to resolve the conflict between extreme ease of use and completeness of data being captured.
It also is supposed to try to be beautiful to the eye.
This is a design-ish document in disarray (everything in random order), accompanied with some code to support it.
Nothing to speak of, yet – mostly just ideas, with some technological groundwork done, like FRP & visualisation: reflex-glfw, and a very basic LambdaCube3d setup for a 2.5D UI.
This is running in several directions at once, with the obvious caveats applicable.
- Map the entire resident context (headspace) available in a convenient way
- Facilitate context switching by introducing sub-contexts
- Structure-derived UI, with triple-store-based ontologies
-
- Felix: Stumbled upon https://github.com/AshleyYakeley/Truth, made me think of you and holotype.
- Self-discoverable expert cli interfaces
-
- Felix: if you ever wondered what’s the state of the art regarding self-discoverable expert cli interfaces: https://kakoune.org/ <= this editor is crazy good regarding that
- Mendix (low-code)
- ECS
- GraphQL
-
- hasura
- https://hasura.io
- http://graphdrawing.org/
- 2010
-
- doi:10.1007/978-3-642-18469-7
- http://www.graphdrawing.org/gd2010/accepted.html
- 2014
-
- doi:10.1007/978-3-662-45803-7
- 2015
- http://www.csun.edu/gd2015/
- doi:10.1007/978-3-319-27261-0
- http://www.csun.edu/gd2015/accepted.htm
- 2016
- https://arxiv.org/html/1609.02443v1
- http://users.encs.concordia.ca/~haarslev/publications/
- flexbox layout implementations
-
- https://github.com/xamarin/flex/blob/master/flex.c
- initial re-implementation:
b77ebb29d6e9c34a4d998d3eacacde50f95ba073
- 2017-10-24 17:35:55
- “update document to follow recent changes”
- initial re-implementation:
- https://github.com/randrew/layout/blob/master/layout.h
- https://github.com/xamarin/flex/blob/master/flex.c
- Sketchpad
Ivan Sutherland’s Sketchpad demo - Object oriented graphics using a constraint based system (https://www.youtube.com/watch?v=6orsmFndx_o)
Douglas Englebart’s demo - too many innovations to list, but includes real time collaboration (he demoed in a convention center while the system ran 30 miles away in his lab, connected by a leased line operating at 1200 baud!). People think he just invented the mouse, but the overarching theme in his work was augmenting human capabilities… http://dougengelbart.org/firsts/dougs-1968-demo.html
- Arrows move
- +modifiers = …
- Space pages
- Tab cycles state/mode
- Escape pops
- Return vs. C-Return – a story of multi-line text input
- “give me more data about this object”
- scaling
- points-of-interest at low scale
- Alt-hold-like extension of details shown
- scaling
- sources & foci factoring out common information from data, which is allowed
to remain non-enriched – which makes sources+foci be like functions
- which leads to need for “pinning” values of those projections, for those values the user considered important
- “well-behaved”: process large datasets efficiently (lazily, if needed)
- a multiple set co-reduction/co-projection model
- a path language derived from above
- external application embedding (WM-like)
- reify query results as projection called stage, that is out of sync by definition
- be very clear about running external processes: can be very frustrating to not know what happens
- versioning
- open sums
- stupid & reliable distributed ACID DB:
- reliable ephemeral identification for tag overlays
- How to pin overlaid metadata to source data – there are sources we have no structural (or even mutation) control over, so can’t pin “within” the data.
- model does not cover data mutability
- source
- 2016 Dexter, Liu, Chau - Lazy Graph Processing in Haskell
- conclusion
- not ready for consumption, according to authors
- source
- 2009 Gill - Type-Safe Observable Sharing in Haskell
- conclusion
- specific tool for discharging direct object references
- source
- 2005 Ramsey, Dias - An Applicative Control-Flow Graph Based on Huet’s Zipper
- source
- 2010 Ramsey, Dias, Peyton Jones - Hoopl: A Modular, Reusable Library for Dataflow Analysis and Transformation
- key properties
-
- unclear improvement over simpler encoding
- source
- https://jaspervdj.be/posts/2017-01-17-lazy-io-graphs.html
- key properties
-
unsafeInterleaveIO
-driven SQL peeking- direct object references
- source
- https://wiki.haskell.org/The_Monad.Reader/Issue5/Practical_Graph_Handling
- key properties
A simple map of node ids to nodes.
halive- [ ] fix lukexi/halive#22
- type
-
- structure
- identification across persistence
- only for metadata-external types, to enable tag overlays
- rendering
-
- meaningful views
- metadata externality
-
- local to data sources
- overlaid from specialized storage
- source types
-
- by structure
- tagged sets
- hierarchies
- file system
- graphs
- element types
-
- by structure
-
- atomic
- (point with attributes)
- meta
- media
- complex
-
- outlines
- org
- graph files
- graphml
- yEd graphml: find definition for tuura/pangraph#7 (comment)
- vue
- graphml
- outlines
- should support rich (schema-capable, version-capable) semantics
- Select
- filter stores through
Selector
, yieldSelection
- Choose presenting engine
- emphasize user agency, deemphasize static rules like defaulting
- context?
- Visibility constraint computation
- engine decides on how much can be shown
- Viewport positioning
- engine decides how to place the view around focus
- Viewport culling
- engine decides on what elements fit into the chosen view
- Layout
- obtain what is already covered, cover what isn’t, compose; compute scene modifiers
- Render
- …
- select
- Structure struc ⇒ Source → Selector struc → Selection struc
- compute_cull
- Presenter struc eng ⇒ eng → (Granularity, MinSize) → Cull eng
- place_viewport
- Presenter struc eng ⇒ eng → Selection struc → Focus struc → Cull eng → Viewport eng
- cull_selection
- Presenter struc eng ⇒ eng → Selection struc → ViewArgs → Viewport eng → (View struc, Boundary eng
- layout
- Presenter struc eng ⇒ eng → (View struc, Boundary eng) → (Layout eng, Ephemeral eng)
- render
- RenderContext ren ⇒ ren → (View struc, Boundary eng) → (Layout eng, Ephemeral eng) → IO ()
- interact
- InputSys is ⇒ is → (View struc, Boundary eng) → Affective → Affective
- Select
- Source → Selector → Selection
- What
- select from Source
Selections
split into the following categories, by structure:- General graph
- DAG (directed acyclic graph)
- Set – with customisable ordering
- XXX: ordering not factored in
- Design considerations
-
- XXX: live-updating selections
- just carry update frequency for re-selection? (DONE)
- any kind of policy that would be more.. reactive?
- XXX: partial selections?
- what for?
- for hopelessly large data sets we can limit
- but a dumb cutoff isn’t useful
- so, a smart, movable cutoff is needed
- for hopelessly large data sets we can limit
- what for?
- does it make sense for a selector to be non-specific about what it returns?
- hard to say just yet, we need experience as guide
- XXX: live-updating selections
- Presenter choice
- Selection → PresPref → Presenter
PresPref
picks a specificPresenter
, compatible with the currentSelection
structure:- defaults to last used
- size limits for non-partial-capable engines?
- can be cycled through by a shortcut
- Engines:
- Graph, dag, tree:
SideGraph
: graph from asideDownGraph
: graph, arrow aligned weighted partitioning
- Dag (duplicates-encoded), tree:
DagList
, list entriesDagGrid
, icon gridDagSpace
, space partitioning, ala Lamdu
- Set:
Carousel
Grid
List
- Graph, dag, tree:
- Summing up, fundamentally we want:
- type classes for individual LEs, because it allows for a seriously neat organisation of code
- multiple LEs associated with a structure, because that’s how the problem domain looks
- #1 gives that there isn’t a monotype for a LE
- Visibility constraints computation
- Presenter → (Granularity, MinSize) → Cull
- disconnected from specific elements – deals with UI constraints projected onto a
specific layout engine:
- for SideGraph and SideDag – no idea, let practice guide us..
- for space partitioning it’s trivial – granularity says it all
- for a Grid and DagGrid – how many rows and columns
- for a List – how many rows
- updated only rarely – when the user changes the visualisation parameters
- disconnected from specific elements – deals with UI constraints projected onto a
specific layout engine:
- Viewport positioning
- Presenter → Selection → Focus → Cull → Viewport
- How do we position a viewport?
- If we don’t have a focus, then it wouldn’t make sense to have a viewport
- Pick a “first” element (maybeHead $ fromList set, e.g.)
- If we don’t have a viewport, generate one containing the focus
- if we do have a viewport, and the focus is inside – choice is upon the engine
- if we the focus is outside, shift the viewport – how exactly is upon the engine
- If we don’t have a focus, then it wouldn’t make sense to have a viewport
- The above exposes following questions:
- what does “inside a viewport” mean?
- how can we generate a viewport that is guaranteed to contain a focus?
- The answer seems to have the shape of a structure-specific visibility
constraint specifier – a
Cull
.
- How do we position a viewport?
- Viewport culling
- Presenter → Selection → (Granularity, MinSize) → Viewport → (View, Boundary)
- XXX: what’s the story about half-visible objects?
- select all intersecting, render more than what is showable?
- XXX: what’s the story about avoidable layout recomputation?
- key question: is it bad? In case of SideGraph, which is about total representation, it’s very very bad.
- caseanalysis cacheable total-cost can-partial partial-composable
- SideGraph: yes very hard no(?) no(?)
- DownGraph: no medium-small yes yes
- DagList: yes small yes yes
- DagGrid: yes medium-small yes yes
- DagSpace: yes very hard yes yes
- Carousel: no easy no no
- Grid: yes easy yes yes
- List: yes easy yes yes
- option: compute base layout, then viewportcull and localise from base
- for huge selections this produces unnecessary computation
- option: go with partials and compose them, whenever possible
- if so, layout needs to be:
- restartable at arbitrary point
- splittable and composable
- if so, layout needs to be:
- option: lazy evaluation?
- NOTE: all obvious caching solutions seem to rely on Ord
Granularity
determines, for tree layouts, the maximum depth of subdivision, after which abbreviation is engagedMinSize
limits the minimum element sizeViewport
is specific toPresenter
:- SideGraph: layout-global position
- DownGraph: subroot node
- DagList: row offset
- DagGrid: row offset
- DagSpace: vertical offset (it’s possible, because it’s weighted space partitioning, but…?)
- Carousel: current selection
- Grid: row offset
- List: row offset
View
is direct elements fromSelection
Boundary
is anchor points to the parts ofSelection
that fall outside theViewport
- XXX: what’s the story about half-visible objects?
- Layout
- Presenter → (View, Boundary) → (Layout, Ephemerals)
- XXX:
Positions
what are they?- scene-specific structure and interpretation?
- if not, global or screenspace?
- pixel-based, or [0.0..1.0]?
Ephemerals
are inherently non-persistent, layout-specific things like:- element focus visulalisation state:
- scale change, to indicate foreground/background
- element focus visulalisation state:
- XXX:
- Change summary
-
- What effect did the last
Selector
change have? Not always obvious.
- What effect did the last
- Render
- RenderContext → (View, Boundary) → (Layout, Ephemerals) → IO ()
- Interaction
- Inputs → (View, Boundary) → Focus → (Granularity, MinSize) → Selector → PresPref → (Modifiers, Focus, (Granularity, MinSize), Selector, PresPref)
- Graphs
-
- Views
-
- Z-axis
-
- Classic side view
- Needs root detection, for automatic layout.
- Arrow-aligned
-
- Weighted partitioning
- Dags
-
- Views
-
- Z-axis
- inherited from Graphs
- Y-axis
- inherited from Graphs
- Treeview, list entries, with duplication
- Treeview, icon grid, with duplication
- Treeview, space partitioning, ala Lamdu, with duplication
- Subsetting
-
- Viewport
- Arrow walker – for nodes. Iterative refinement – subsetting and context narrowing. Some kind of a shortcut-based jump language. Bookmarks.
- Ellipsis
- Zoomable: “everything else in this direction” What cases need it, given a proper Viewport subsetter?
- Sets
-
- Views
-
- Carousel
- Grid
- List
- Subsetting
-
- Viewport
- Iterative refinement makes it useful. Arrow walker – for refinement elements and for.
- Summary
- Extracting and exposing set structure.
- Ellipsis
- Logic summary or an explicit summary.
- Exhaustivity
-
- Explicit “unknown” remaining
- Variant-ness
-
- Simultaneous
- Per-choice filtering
- Progression
- Distinctions
-
- Decomposition vs. dependency
..and associated operations
- fairly flexible font selection with aliases, vector/bitmap distinction, variants and defaulting
- LambdaCube3D-based, so richly extensible
- picking supported
- 2.5D
- screen/frame management
- targetable by HoloCairo
- vocabulary:
- As
- a Name that Denotes a type
- Interp
- Interpret a type into another
- Mutable
- evolution in response to events subscribed to
- Holo
- build upon the above – mix input events with others to define a dynamic As/Interp-defined interactive widget
- why did we (mistakenly) go with: (As a, As b) => As (a, b)
- originally: -> Holo (Di a)
- also: Denoted n ~ (a, a)
- the mistake of (As a, As b) => As (a, b) – necessitates own, intra-widget focus management, since specialised input is impossible due to genericity
- let’s go back to generic Holo (Di a)
- does it need an As n, Denoted n ~ a, Interp a (Di b)?
- What is implementable/not for a multi-Identity composite?
- [-] As n, Denoted n ~ Composite – necessitates a single Identity
- [-] Mutable Composite – we have a multitude of identities and want to reuse generic focus machinery
- [X] Named Composite b
- [X] Interp Composite b
- prerequisite lift step doable generically via liftWRecord on (,)
- let’s turn liftWRecord into a Holo instance?
- ..would require As
- -> impossible?
- generic: monadically recovers a datatype from the structure of a related datatype, with relationship treated in applicative context
- allows us to lift single-product records into editable widgets
- t & m that liftWRecord depends on are ambiguous
- must be somehow deduced from the Holo’s head-bound vars
- -ddump-tc-trace
- -dcore-lint
- width, height ∷ float – absolute-only?
- left, right, top, bottom ∷ float – def(0), ???
- padding_LRTB, margin_LRTB ∷ float – def(0)
- justify_content ∷ def(
ALIGN_START
) - align_content ∷ def(
ALIGN_STRETCH
) - align_items ∷ def(
ALIGN_START
) - align_self ∷ def(
ALIGN_AUTO
) - position ∷ def(
POSITION_RELATIVE
) - direction ∷ def(
DIRECTION_COLUMN
) - wrap ∷ def(
NO_WRAP
) - grow ∷ def(0)
- shrink ∷ def(1)
- order ∷ def(0)
- basis ∷ def(0)
- …attributes (see above) ∷ xxx
- frame ∷ float[4]
- parent ∷ ptr flex_item
- children ∷ [ptr flex_item]
- should_order_children ∷ bool
- set during init
- wrap ∷ bool
- reverse ∷ bool – whether main axis is reversed
- reverse2 ∷ bool – whether cross axis is reversed (wrap only)
- vertical ∷ bool
- size_dim ∷ float – main axis parent size
- align_dim ∷ float – cross axis parent size
- frame_pos_i ∷ uint – main axis position
- frame_pos2_i ∷ uint – cross axis position
- frame_size_i ∷ uint – main axis size
- frame_size2_i ∷ uint – cross axis size
- ordered_indices ∷ [int]
- set for each line layout
- line_dim ∷ float – the cross-axis size
- flex_dim ∷ float – the flexible part of the main axis size
- flex_grows ∷ int
- flex_shrinks ∷ int
- pos2 ∷ float – cross axis position
- lines ∷ [struct flex_layout_line]
- child_begin ∷ uint
- child_end ∷ uint
- size ∷ float
- lines_count ∷ uint
- lines_sizes ∷ float
- update_should_order_children() ∷ set parent’s should_order_children to true
- item_property_changed(property) ∷ property ≡ order → update_should_order_children
- flex_item_new/free() ∷ malloc + default attributes & stuff / free() children, then self
*******
- grow_if_needed ∷ flex_item → void
- child_set ∷ flex_item → flex_item → int → void
- flex_item_add ∷ flex_item → flex_item → void
- flex_item_insert ∷ flex_item → void
- flex_item_delete ∷ flex_item → flex_item
- flex_item_count ∷ flex_item → uint
- flex_item_child ∷ flex_item → flex_item
- flex_item_parent ∷ flex_item → flex_item
- flex_item_root ∷ flex_item → flex_item
- flex_item_get_framex,y,width,height ∷ flex_item → float
*******
- layout_init ∷ flex_item → float → float → flex_layout → void
let width/height = args.w/args.h - item→padding_left - item→padding_right
(,,,,)
reverse vertical
size_dim align_dim
frame_pos{,2}_i
frame_size{,2}_i
= case item→direction of
DIRECTION_ROW_REVERSE | f width height
DIRECTION_ROW |
DIRECTION_COLUMN_REVERSE |
DIRECTION_COLUMN |
ordered_indices = | f item→should_order_children
item→children_count item→children – sorted children indices by their .order
propertyflexdim,grows,shrinks = (,,) 0 0 0 wrap = item→wrap != NO_WRAP (,) pos2 reverse2 = | f wrap item→wrap
align_dim vertical item→padding_top lines = [] in Layout{..}
- layout_cleanup ∷ flex_layout → void
*******
- LAYOUT_RESET ∷ flex_layout → flex_layout layout & line_dim .~ if wrap then 0 else align_dim & flex_dim .~ size_dim & flex_grows .~ 0 & flex_shrinks .~ 0
- _LAYOUT_FRAME ∷ layout → child → {pos,pos2,size,size2} → float
- CHILD_POS, CHILD_POS2, CHILD_SIZE, CHILD_SIZE2 = _LAYOUT_FRAME(…)
- CHILD_MARGIN ∷ child → if_vertical ∷ bool → if_horizontal ∷ bool →
*******
- layout_align ∷ align ∷ flex_align → flex_dim ∷ float → children_count ∷ uint → pos_p ∷ ptr float → spacing_p ∷ ptr float → stretch_allowed ∷ bool
- child_align ∷ child ∷ flex_item → parent ∷ flex_item → flex_align
- layout_items ∷ item ∷ flex_item → child_begin ∷ uint → child_end ∷ uint → children_count ∷ int → layout ∷ flex_layout → void
- layout_item ∷ item ∷ flex_item → width ∷ float → height ∷ float
- flex_layout ∷ item ∷ flex_item → void
Note: this leaves view-porting (as an overflow handling mechanism) out of scope for now.
Leaf | Modifier | FromTop | Style | Hardness | ToTop | Shrink method | Notes | ||
---|---|---|---|---|---|---|---|---|---|
Text | One-line | () | font, unbreak | Hard | Abs | no | |||
() | font, unbreak | Soft | Rel | ellipsis | |||||
Breakable | AbsCstr | font | Soft | Rel | ellipsis | hard breakable is ⊥ | |||
Image | Fixed | () | fixed | Hard | Abs | no | soft fixed image is ⊥, unless viewporting | ||
Scalable | AbsCstr | () | Soft | Rel | scale | ||||
??? | any other leaf types? | ||||||||
Inter | Modifier | FromTop | Style | FromBot | ToBot | Hardness Honoring | ToTop | Notes | |
Box | AbsCstr | () | Abs/Rel | ? | Rebalancing | ||||
Wrap | AbsCstr | thickness | Abs/Rel | ? | ????????????????? |
Apparent fallout from fundamentals ∷
- Hard requirements are naturally context-free
- Relative hards are possible, though (ratios being a question of design)
- Context-ful requirements are impossible up-front, in a single pass
Observations ∷
- Child ratio knowledge is minimum for Box’s downward propagation of AbsCstr
- Some children don’t have ratios, but absolutes can be relativised (absolutisation of relatives is a feasible dual that can lead to better pixel-level stability)
- #1 + #2 → child ratios always available, and always immediately – assuming no inter-level balancing
Box hardness honoring procedure ∷
- Query all children for direct requirements
- Allocate hards (absolutising relatives), computing the remaining soft share
- Relativise all soft absolutes from #1
- When softs sum to overflow, normalise them
- When softs sum to underflow, normalise them, unless there are filler children
- Absolutise softs back
- When there’s underflow and fillers, distrubute slack between fillers
- ??? hards overflow handling policy
- ideally, propagate upward
- as a “lacks absolute N”?
- ideally, propagate upward
Summary: hards first, then redistribute remainder while keeping fillers in mind.
Wrap hardness honoring procedure ∷
- Query children for direct requirements
- Absolutes that fit exactly: easy
- Absolutes that underflow: ???
- Absolutes that overflow:
- ideally, propagate upward (see same for box hardness)
- Make Item sport the content type by default, only wrapping it for children.
- Bonus: make Item into an HList-like structure!
- that has a risk of making refactoring super-painful in the current prototyping phase, though.
- ct = (!! choice) $ SOP.apInjs_POP pop ∷ SOP (m :.: Result t) xss
- By hsequence ∷ (SListIN h xs, SListIN (Prod h) xs, HSequence h, Applicative f) => h f xs -> f (h I xs)
- Comp msop = hsequence ct ∷ (m :.: Result t) (SOP I xss)
- By unComp ∷ (f :.: g) p -> f (g p)
- msop ∷ m (Result t (SOP I xss))
- By (SOP.to <$>) <$> m (Result t (SOP I (x : xs))) -> m (Result t a)
- Comp res = (SOP.to <$>) <$> msop ∷ (m :.: Result t) a
- By unComp ∷ (f :.: g) p -> f (g p)
- res ∷ m (Result t a)
- hsequence’ ∷ (SListIN h xs, Applicative f) ⇒ h (f :.: g) xs → f (h g xs) hsequence ∷ h f xs → f (h I xs) htraverse’ ∷ (SListI2 xss, Applicative g) => (forall a ⇒ f a → g (f’ a)) → SOP f xss → g (SOP f’ xss)
- g ~ m, f’ ~ Result t
- (forall a ⇒ f a → m (Result t a)) → SOP f xss → m (SOP (Result t) xss)
- forall f xs. (c a, All c xs, All2 c xss) ⇒ … → (a → f) → (m :.: Result t) f
- We want to separate (Result t f) from f
- Theory: can we go for this instead: → m (Result t f)
- Let’s try!
- introduce a separate node finalisation post-phase, that performs monadic computation after the ADT lift
- cabal-doctest
- [X] fetchpatch
- cabal-install
- [X] bump for Cabal
- generic-deriving
- [X] bump for TH
- data-default
- [ ] ???
- [“cp”, “-f”, ”nix/store/baa1q5ph9w2daw63vdblldq4wc5g74c0-data-default-class-0.1.2.0/lib/ghc-8.7.20190115/package.conf.d/data-default-class-0.1.2.0-FeIQ5tLoVZBHMSgrT9zptQ.conf”, “/build/package.conf.d”]
- [“cp”, “-f”, ”build/package.conf.d”]
- [ ] ???
- haskell-src-exts
- [ ] sigsegv in Haddock
- gtk2hs-buildtools
- [ ] MonadFail gtk2hs/gtk2hs#264
- th-lift
- [ ] TH RyanGlScott/th-lift#38
- generics-sop
- [ ] TH well-typed/generics-sop#96
;; Local Variables: ;; eval: (setf indent-tabs-mode nil org-todo-keyword-faces ‘((“TODO” . “#6c71c4”) (“START” . “#2aa198”) (“CODE” . “#6c71c4”) (“SORTA” . “#268bd2”) (“DONE” . “#073642”) (“UPSTREAM” . “#268bd2”))) ;; End: