Repository files navigation

lens Build StatuscodecovScribble Docs

A Racket package for creating and composing pure functional lenses.

raco pkg install lens(require lens)

What on earth are lenses?

A lens is a value that can be used to focus on a small subpiece of some larger structure. A lens splits some data structure into two pieces - a view, which is some small isolated component of the data structure, and a context, which is everything else. The context can have a new view placed into it. This makes a lens act like a pure functional getter and setter:

> (lens-view first-lens '(123))
1
> (lens-set first-lens '(123) 'a)
'(a 23)

Lenses are first class values and pure functional, so they can be abstracted over and functions that operate on lenses can be created. For instance, given a lens its view can be "updated":

> (lens-transform first-lens '(123) number->string)
'("1"23)

Additionaly, lenses are separate values from the objects they operate on, so they can be manipulated independently of any specific data. Functions can construct lenses, and operations can combine lenses. This allows for lens composition:

> (define first-of-b-key-lens (lens-compose first-lens (hash-ref-lens 'b)))
> (define a-hash (hash 'a '(123) 'b '(102030) 'c '(100200300)))
> (lens-view first-of-b-key-lens a-hash)
10
> (lens-set first-of-b-key-lens a-hash 'foo)
#hash((a . (123)) (b . (foo 2030)) (c . (100200300)))

Lenses can also be joined together to form compound lenses that view many things:

> (define first-third-fifth-lens (lens-join/list first-lens third-lens fifth-lens))
> (lens-view first-third-fifth-lens '(123456))
'(135)
> (lens-set first-third-fifth-lens '(123456) '(a b c))
'(a 2 b 4 c 6)

Lenses can also be extended to operate on some new data structure:

> (define first-of-each-lens (map-lens first-lens))
> (lens-view first-of-each-lens '((12) (34) (56)))
'(135)
> (lens-set first-of-each-lens '((12) (34) (56)) '(a b c))
'((a 2) (b 4) (c 6))

See the documentation for a full API reference

So when would I want to use lenses?

Lenses are most effective when you're dealing with the "giant ball of state" problem. When you have a large amount of state you need to pass around between code written in a functional style, it's difficult to update and manage it due to the lack of mutation "magically" updating your entire object graph when a function changes a small part of it. Lenses allow code to break down and manipulate portions of this state, simplifying interactions and updates.

In particular, consider using lenses if you find yourself doing any of the following:

  • Using a giant complex piece of state that most pieces of code only care about a small part of
  • Writing struct-copy a lot
  • Converting some hairy data structure into another one, manipulating it, then turning it back
  • Wishing you could treat data X as if it were a Y, i.e. "I wish this struct was a list so I could map over it easily"
  • Creating structs that have nested struct instances inside them.

For a more in depth introduction, see The Lens Guide. For detailed API documentation, see The Lens Reference.

About

A Racket package for creating and composing pure functional lenses

Topics

Resources

Stars

79 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

lens Build StatuscodecovScribble Docs

A Racket package for creating and composing pure functional lenses.

raco pkg install lens(require lens)

What on earth are lenses?

A lens is a value that can be used to focus on a small subpiece of some larger structure. A lens splits some data structure into two pieces - a view, which is some small isolated component of the data structure, and a context, which is everything else. The context can have a new view placed into it. This makes a lens act like a pure functional getter and setter:

> (lens-view first-lens '(123))
1
> (lens-set first-lens '(123) 'a)
'(a 23)

Lenses are first class values and pure functional, so they can be abstracted over and functions that operate on lenses can be created. For instance, given a lens its view can be "updated":

> (lens-transform first-lens '(123) number->string)
'("1"23)

Additionaly, lenses are separate values from the objects they operate on, so they can be manipulated independently of any specific data. Functions can construct lenses, and operations can combine lenses. This allows for lens composition:

> (define first-of-b-key-lens (lens-compose first-lens (hash-ref-lens 'b)))
> (define a-hash (hash 'a '(123) 'b '(102030) 'c '(100200300)))
> (lens-view first-of-b-key-lens a-hash)
10
> (lens-set first-of-b-key-lens a-hash 'foo)
#hash((a . (123)) (b . (foo 2030)) (c . (100200300)))

Lenses can also be joined together to form compound lenses that view many things:

> (define first-third-fifth-lens (lens-join/list first-lens third-lens fifth-lens))
> (lens-view first-third-fifth-lens '(123456))
'(135)
> (lens-set first-third-fifth-lens '(123456) '(a b c))
'(a 2 b 4 c 6)

Lenses can also be extended to operate on some new data structure:

> (define first-of-each-lens (map-lens first-lens))
> (lens-view first-of-each-lens '((12) (34) (56)))
'(135)
> (lens-set first-of-each-lens '((12) (34) (56)) '(a b c))
'((a 2) (b 4) (c 6))

See the documentation for a full API reference

So when would I want to use lenses?

Lenses are most effective when you're dealing with the "giant ball of state" problem. When you have a large amount of state you need to pass around between code written in a functional style, it's difficult to update and manage it due to the lack of mutation "magically" updating your entire object graph when a function changes a small part of it. Lenses allow code to break down and manipulate portions of this state, simplifying interactions and updates.

In particular, consider using lenses if you find yourself doing any of the following:

  • Using a giant complex piece of state that most pieces of code only care about a small part of
  • Writing struct-copy a lot
  • Converting some hairy data structure into another one, manipulating it, then turning it back
  • Wishing you could treat data X as if it were a Y, i.e. "I wish this struct was a list so I could map over it easily"
  • Creating structs that have nested struct instances inside them.

For a more in depth introduction, see The Lens Guide. For detailed API documentation, see The Lens Reference.

About

A Racket package for creating and composing pure functional lenses

Topics

Resources

Stars

79 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

lens Build StatuscodecovScribble Docs

A Racket package for creating and composing pure functional lenses.

raco pkg install lens(require lens)

What on earth are lenses?

A lens is a value that can be used to focus on a small subpiece of some larger structure. A lens splits some data structure into two pieces - a view, which is some small isolated component of the data structure, and a context, which is everything else. The context can have a new view placed into it. This makes a lens act like a pure functional getter and setter:

> (lens-view first-lens '(123))
1
> (lens-set first-lens '(123) 'a)
'(a 23)

Lenses are first class values and pure functional, so they can be abstracted over and functions that operate on lenses can be created. For instance, given a lens its view can be "updated":

> (lens-transform first-lens '(123) number->string)
'("1"23)

Additionaly, lenses are separate values from the objects they operate on, so they can be manipulated independently of any specific data. Functions can construct lenses, and operations can combine lenses. This allows for lens composition:

> (define first-of-b-key-lens (lens-compose first-lens (hash-ref-lens 'b)))
> (define a-hash (hash 'a '(123) 'b '(102030) 'c '(100200300)))
> (lens-view first-of-b-key-lens a-hash)
10
> (lens-set first-of-b-key-lens a-hash 'foo)
#hash((a . (123)) (b . (foo 2030)) (c . (100200300)))

Lenses can also be joined together to form compound lenses that view many things:

> (define first-third-fifth-lens (lens-join/list first-lens third-lens fifth-lens))
> (lens-view first-third-fifth-lens '(123456))
'(135)
> (lens-set first-third-fifth-lens '(123456) '(a b c))
'(a 2 b 4 c 6)

Lenses can also be extended to operate on some new data structure:

> (define first-of-each-lens (map-lens first-lens))
> (lens-view first-of-each-lens '((12) (34) (56)))
'(135)
> (lens-set first-of-each-lens '((12) (34) (56)) '(a b c))
'((a 2) (b 4) (c 6))

See the documentation for a full API reference

So when would I want to use lenses?

Lenses are most effective when you're dealing with the "giant ball of state" problem. When you have a large amount of state you need to pass around between code written in a functional style, it's difficult to update and manage it due to the lack of mutation "magically" updating your entire object graph when a function changes a small part of it. Lenses allow code to break down and manipulate portions of this state, simplifying interactions and updates.

In particular, consider using lenses if you find yourself doing any of the following:

  • Using a giant complex piece of state that most pieces of code only care about a small part of
  • Writing struct-copy a lot
  • Converting some hairy data structure into another one, manipulating it, then turning it back
  • Wishing you could treat data X as if it were a Y, i.e. "I wish this struct was a list so I could map over it easily"
  • Creating structs that have nested struct instances inside them.

For a more in depth introduction, see The Lens Guide. For detailed API documentation, see The Lens Reference.

About

A Racket package for creating and composing pure functional lenses

Topics

Resources

Stars

79 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

lens Build StatuscodecovScribble Docs

A Racket package for creating and composing pure functional lenses.

raco pkg install lens(require lens)

What on earth are lenses?

A lens is a value that can be used to focus on a small subpiece of some larger structure. A lens splits some data structure into two pieces - a view, which is some small isolated component of the data structure, and a context, which is everything else. The context can have a new view placed into it. This makes a lens act like a pure functional getter and setter:

> (lens-view first-lens '(123))
1
> (lens-set first-lens '(123) 'a)
'(a 23)

Lenses are first class values and pure functional, so they can be abstracted over and functions that operate on lenses can be created. For instance, given a lens its view can be "updated":

> (lens-transform first-lens '(123) number->string)
'("1"23)

Additionaly, lenses are separate values from the objects they operate on, so they can be manipulated independently of any specific data. Functions can construct lenses, and operations can combine lenses. This allows for lens composition:

> (define first-of-b-key-lens (lens-compose first-lens (hash-ref-lens 'b)))
> (define a-hash (hash 'a '(123) 'b '(102030) 'c '(100200300)))
> (lens-view first-of-b-key-lens a-hash)
10
> (lens-set first-of-b-key-lens a-hash 'foo)
#hash((a . (123)) (b . (foo 2030)) (c . (100200300)))

Lenses can also be joined together to form compound lenses that view many things:

> (define first-third-fifth-lens (lens-join/list first-lens third-lens fifth-lens))
> (lens-view first-third-fifth-lens '(123456))
'(135)
> (lens-set first-third-fifth-lens '(123456) '(a b c))
'(a 2 b 4 c 6)

Lenses can also be extended to operate on some new data structure:

> (define first-of-each-lens (map-lens first-lens))
> (lens-view first-of-each-lens '((12) (34) (56)))
'(135)
> (lens-set first-of-each-lens '((12) (34) (56)) '(a b c))
'((a 2) (b 4) (c 6))

See the documentation for a full API reference

So when would I want to use lenses?

Lenses are most effective when you're dealing with the "giant ball of state" problem. When you have a large amount of state you need to pass around between code written in a functional style, it's difficult to update and manage it due to the lack of mutation "magically" updating your entire object graph when a function changes a small part of it. Lenses allow code to break down and manipulate portions of this state, simplifying interactions and updates.

In particular, consider using lenses if you find yourself doing any of the following:

  • Using a giant complex piece of state that most pieces of code only care about a small part of
  • Writing struct-copy a lot
  • Converting some hairy data structure into another one, manipulating it, then turning it back
  • Wishing you could treat data X as if it were a Y, i.e. "I wish this struct was a list so I could map over it easily"
  • Creating structs that have nested struct instances inside them.

For a more in depth introduction, see The Lens Guide. For detailed API documentation, see The Lens Reference.

About

A Racket package for creating and composing pure functional lenses

Topics

Resources

Stars

79 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

lens Build StatuscodecovScribble Docs

A Racket package for creating and composing pure functional lenses.

raco pkg install lens(require lens)

What on earth are lenses?

A lens is a value that can be used to focus on a small subpiece of some larger structure. A lens splits some data structure into two pieces - a view, which is some small isolated component of the data structure, and a context, which is everything else. The context can have a new view placed into it. This makes a lens act like a pure functional getter and setter:

> (lens-view first-lens '(123))
1
> (lens-set first-lens '(123) 'a)
'(a 23)

Lenses are first class values and pure functional, so they can be abstracted over and functions that operate on lenses can be created. For instance, given a lens its view can be "updated":

> (lens-transform first-lens '(123) number->string)
'("1"23)

Additionaly, lenses are separate values from the objects they operate on, so they can be manipulated independently of any specific data. Functions can construct lenses, and operations can combine lenses. This allows for lens composition:

> (define first-of-b-key-lens (lens-compose first-lens (hash-ref-lens 'b)))
> (define a-hash (hash 'a '(123) 'b '(102030) 'c '(100200300)))
> (lens-view first-of-b-key-lens a-hash)
10
> (lens-set first-of-b-key-lens a-hash 'foo)
#hash((a . (123)) (b . (foo 2030)) (c . (100200300)))

Lenses can also be joined together to form compound lenses that view many things:

> (define first-third-fifth-lens (lens-join/list first-lens third-lens fifth-lens))
> (lens-view first-third-fifth-lens '(123456))
'(135)
> (lens-set first-third-fifth-lens '(123456) '(a b c))
'(a 2 b 4 c 6)

Lenses can also be extended to operate on some new data structure:

> (define first-of-each-lens (map-lens first-lens))
> (lens-view first-of-each-lens '((12) (34) (56)))
'(135)
> (lens-set first-of-each-lens '((12) (34) (56)) '(a b c))
'((a 2) (b 4) (c 6))

See the documentation for a full API reference

So when would I want to use lenses?

Lenses are most effective when you're dealing with the "giant ball of state" problem. When you have a large amount of state you need to pass around between code written in a functional style, it's difficult to update and manage it due to the lack of mutation "magically" updating your entire object graph when a function changes a small part of it. Lenses allow code to break down and manipulate portions of this state, simplifying interactions and updates.

In particular, consider using lenses if you find yourself doing any of the following:

  • Using a giant complex piece of state that most pieces of code only care about a small part of
  • Writing struct-copy a lot
  • Converting some hairy data structure into another one, manipulating it, then turning it back
  • Wishing you could treat data X as if it were a Y, i.e. "I wish this struct was a list so I could map over it easily"
  • Creating structs that have nested struct instances inside them.

For a more in depth introduction, see The Lens Guide. For detailed API documentation, see The Lens Reference.

About

A Racket package for creating and composing pure functional lenses

Topics

Resources

Stars

79 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

lens Build StatuscodecovScribble Docs

A Racket package for creating and composing pure functional lenses.

raco pkg install lens(require lens)

What on earth are lenses?

A lens is a value that can be used to focus on a small subpiece of some larger structure. A lens splits some data structure into two pieces - a view, which is some small isolated component of the data structure, and a context, which is everything else. The context can have a new view placed into it. This makes a lens act like a pure functional getter and setter:

> (lens-view first-lens '(123))
1
> (lens-set first-lens '(123) 'a)
'(a 23)

Lenses are first class values and pure functional, so they can be abstracted over and functions that operate on lenses can be created. For instance, given a lens its view can be "updated":

> (lens-transform first-lens '(123) number->string)
'("1"23)

Additionaly, lenses are separate values from the objects they operate on, so they can be manipulated independently of any specific data. Functions can construct lenses, and operations can combine lenses. This allows for lens composition:

> (define first-of-b-key-lens (lens-compose first-lens (hash-ref-lens 'b)))
> (define a-hash (hash 'a '(123) 'b '(102030) 'c '(100200300)))
> (lens-view first-of-b-key-lens a-hash)
10
> (lens-set first-of-b-key-lens a-hash 'foo)
#hash((a . (123)) (b . (foo 2030)) (c . (100200300)))

Lenses can also be joined together to form compound lenses that view many things:

> (define first-third-fifth-lens (lens-join/list first-lens third-lens fifth-lens))
> (lens-view first-third-fifth-lens '(123456))
'(135)
> (lens-set first-third-fifth-lens '(123456) '(a b c))
'(a 2 b 4 c 6)

Lenses can also be extended to operate on some new data structure:

> (define first-of-each-lens (map-lens first-lens))
> (lens-view first-of-each-lens '((12) (34) (56)))
'(135)
> (lens-set first-of-each-lens '((12) (34) (56)) '(a b c))
'((a 2) (b 4) (c 6))

See the documentation for a full API reference

So when would I want to use lenses?

Lenses are most effective when you're dealing with the "giant ball of state" problem. When you have a large amount of state you need to pass around between code written in a functional style, it's difficult to update and manage it due to the lack of mutation "magically" updating your entire object graph when a function changes a small part of it. Lenses allow code to break down and manipulate portions of this state, simplifying interactions and updates.

In particular, consider using lenses if you find yourself doing any of the following:

  • Using a giant complex piece of state that most pieces of code only care about a small part of
  • Writing struct-copy a lot
  • Converting some hairy data structure into another one, manipulating it, then turning it back
  • Wishing you could treat data X as if it were a Y, i.e. "I wish this struct was a list so I could map over it easily"
  • Creating structs that have nested struct instances inside them.

For a more in depth introduction, see The Lens Guide. For detailed API documentation, see The Lens Reference.

About

A Racket package for creating and composing pure functional lenses

Topics

Resources

Stars

79 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

lens Build StatuscodecovScribble Docs

A Racket package for creating and composing pure functional lenses.

raco pkg install lens(require lens)

What on earth are lenses?

A lens is a value that can be used to focus on a small subpiece of some larger structure. A lens splits some data structure into two pieces - a view, which is some small isolated component of the data structure, and a context, which is everything else. The context can have a new view placed into it. This makes a lens act like a pure functional getter and setter:

> (lens-view first-lens '(123))
1
> (lens-set first-lens '(123) 'a)
'(a 23)

Lenses are first class values and pure functional, so they can be abstracted over and functions that operate on lenses can be created. For instance, given a lens its view can be "updated":

> (lens-transform first-lens '(123) number->string)
'("1"23)

Additionaly, lenses are separate values from the objects they operate on, so they can be manipulated independently of any specific data. Functions can construct lenses, and operations can combine lenses. This allows for lens composition:

> (define first-of-b-key-lens (lens-compose first-lens (hash-ref-lens 'b)))
> (define a-hash (hash 'a '(123) 'b '(102030) 'c '(100200300)))
> (lens-view first-of-b-key-lens a-hash)
10
> (lens-set first-of-b-key-lens a-hash 'foo)
#hash((a . (123)) (b . (foo 2030)) (c . (100200300)))

Lenses can also be joined together to form compound lenses that view many things:

> (define first-third-fifth-lens (lens-join/list first-lens third-lens fifth-lens))
> (lens-view first-third-fifth-lens '(123456))
'(135)
> (lens-set first-third-fifth-lens '(123456) '(a b c))
'(a 2 b 4 c 6)

Lenses can also be extended to operate on some new data structure:

> (define first-of-each-lens (map-lens first-lens))
> (lens-view first-of-each-lens '((12) (34) (56)))
'(135)
> (lens-set first-of-each-lens '((12) (34) (56)) '(a b c))
'((a 2) (b 4) (c 6))

See the documentation for a full API reference

So when would I want to use lenses?

Lenses are most effective when you're dealing with the "giant ball of state" problem. When you have a large amount of state you need to pass around between code written in a functional style, it's difficult to update and manage it due to the lack of mutation "magically" updating your entire object graph when a function changes a small part of it. Lenses allow code to break down and manipulate portions of this state, simplifying interactions and updates.

In particular, consider using lenses if you find yourself doing any of the following:

  • Using a giant complex piece of state that most pieces of code only care about a small part of
  • Writing struct-copy a lot
  • Converting some hairy data structure into another one, manipulating it, then turning it back
  • Wishing you could treat data X as if it were a Y, i.e. "I wish this struct was a list so I could map over it easily"
  • Creating structs that have nested struct instances inside them.

For a more in depth introduction, see The Lens Guide. For detailed API documentation, see The Lens Reference.

About

A Racket package for creating and composing pure functional lenses

Topics

Resources

Stars

79 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

lens Build StatuscodecovScribble Docs

A Racket package for creating and composing pure functional lenses.

raco pkg install lens(require lens)

What on earth are lenses?

A lens is a value that can be used to focus on a small subpiece of some larger structure. A lens splits some data structure into two pieces - a view, which is some small isolated component of the data structure, and a context, which is everything else. The context can have a new view placed into it. This makes a lens act like a pure functional getter and setter:

> (lens-view first-lens '(123))
1
> (lens-set first-lens '(123) 'a)
'(a 23)

Lenses are first class values and pure functional, so they can be abstracted over and functions that operate on lenses can be created. For instance, given a lens its view can be "updated":

> (lens-transform first-lens '(123) number->string)
'("1"23)

Additionaly, lenses are separate values from the objects they operate on, so they can be manipulated independently of any specific data. Functions can construct lenses, and operations can combine lenses. This allows for lens composition:

> (define first-of-b-key-lens (lens-compose first-lens (hash-ref-lens 'b)))
> (define a-hash (hash 'a '(123) 'b '(102030) 'c '(100200300)))
> (lens-view first-of-b-key-lens a-hash)
10
> (lens-set first-of-b-key-lens a-hash 'foo)
#hash((a . (123)) (b . (foo 2030)) (c . (100200300)))

Lenses can also be joined together to form compound lenses that view many things:

> (define first-third-fifth-lens (lens-join/list first-lens third-lens fifth-lens))
> (lens-view first-third-fifth-lens '(123456))
'(135)
> (lens-set first-third-fifth-lens '(123456) '(a b c))
'(a 2 b 4 c 6)

Lenses can also be extended to operate on some new data structure:

> (define first-of-each-lens (map-lens first-lens))
> (lens-view first-of-each-lens '((12) (34) (56)))
'(135)
> (lens-set first-of-each-lens '((12) (34) (56)) '(a b c))
'((a 2) (b 4) (c 6))

See the documentation for a full API reference

So when would I want to use lenses?

Lenses are most effective when you're dealing with the "giant ball of state" problem. When you have a large amount of state you need to pass around between code written in a functional style, it's difficult to update and manage it due to the lack of mutation "magically" updating your entire object graph when a function changes a small part of it. Lenses allow code to break down and manipulate portions of this state, simplifying interactions and updates.

In particular, consider using lenses if you find yourself doing any of the following:

  • Using a giant complex piece of state that most pieces of code only care about a small part of
  • Writing struct-copy a lot
  • Converting some hairy data structure into another one, manipulating it, then turning it back
  • Wishing you could treat data X as if it were a Y, i.e. "I wish this struct was a list so I could map over it easily"
  • Creating structs that have nested struct instances inside them.

For a more in depth introduction, see The Lens Guide. For detailed API documentation, see The Lens Reference.

About

A Racket package for creating and composing pure functional lenses

Topics

Resources

Stars

79 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages