Morwenn posted a blog about implementing a
std::flip
operation in C++:
This is basically walking up the tree from the child node as if it were a linked list. The reverse operation either implies walking through two children nodes, or simply flipping the order of parameters, which is where
std::flip
intervenes:auto is_descendant_of = std::flip(is_ancestor_of); // This property should always hold assert(is_descendant_of(node1, node2) == is_ancestor_of(node2, node1));
Spoiler: the std::flip
operator is not part of the C++ standard
library, although an implementation is
providing at the end of the blog post in around 90 lines of code.
Still, I thought it would be fun to implement in Factor.
As it turns out, we already have a
flip word that
modifies a sequence, essentially by returning the transpose of a matrix. One
could argue that
transpose
might be a better name for that operation. In any event, let’s focus on
implementing the std::flip
operation.
How would we reverse the arguments to a word?
a b
can become b a
by calling swap
.a b c
can become c b a
by calling swap rot
.a b c d
can become d c b a
by calling swap rot roll
.We can generalize this into a macro by repeatedly calling -nrot:
MACRO: nreverse ( n -- quot )
0 [a..b) [ '[ _ -nrot ] ] map [ ] concat-as ;
And then show that it works:
IN: scratchpad { } [ 0 nreverse ] with-datastack .
{ }
IN: scratchpad { "a" } [ 1 nreverse ] with-datastack .
{ "a" }
IN: scratchpad { "a" "b" } [ 2 nreverse ] with-datastack .
{ "b" "a" }
IN: scratchpad { "a" "b" "c" } [ 3 nreverse ] with-datastack .
{ "c" "b" "a" }
IN: scratchpad { "a" "b" "c" "d" } [ 4 nreverse ] with-datastack .
{ "d" "c" "b" "a" }
IN: scratchpad { "a" "b" "c" "d" "e" } [ 5 nreverse ] with-datastack .
{ "e" "d" "c" "b" "a" }
Note: this has been added to the shuffle vocabulary.
Using this, we can build some syntax that takes the next token and searches for a matching word with that name, and then calls it after reversing the inputs:
SYNTAX: flip:
scan-word [ stack-effect in>> length ] keep
'[ _ nreverse _ execute ] append! ;
As an example, we will use the 4array word that returns an array consisting of four arguments from the stack.
IN: scratchpad 10 20 30 40 4array .
{ 10 20 30 40 }
IN: scratchpad 10 20 30 40 flip: 4array .
{ 40 30 20 10 }
We could have different syntax for flipping arbitrary code – first parsing a quotation and then infer the stack-effect and then inline a reversed argument version.
SYNTAX: flip[
parse-quotation [ infer in>> length ] keep
'[ _ nreverse @ ] suffix! ;
We can try that out with a simple block of code:
IN: scratchpad 1 2 3 flip[ [ 10 * ] tri@ ] call 3array .
{ 30 20 10 }
And only a few lines of code in total.
Pretty cool!
Seth Larson wrote about a Scream Cipher:
You’ve probably heard of stream ciphers, but what about a scream cipher 😱? Today I learned there are more “Latin capital letter A” Unicode characters than there are letters in the English alphabet. You know what that means, it’s time to scream:
We can use bidirectional assocs to keep a single cipher data structure that efficiently maps into and out of the cipher:
CONSTANT: cipher $[
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"AÁĂẮẶẰẲẴǍÂẤẬẦẨẪÄǞȦǠẠȀÀẢȂĀĄ"
zip >biassoc
]
: >scream ( str -- SCREAM )
[ ch>upper cipher ?at drop ] map ;
: scream> ( SCREAM -- str )
[ cipher ?value-at drop ] map ;
And then give it a try!
IN: scratchpad "FACTOR!" >scream .
"ẰAĂẠẪȦ!"
IN: scratchpad "ẰAĂẠẪȦ!" scream> .
"FACTOR!"
Fun!
Factor has an environment vocabulary for working with process environment variables on all the platforms we currently support: macOS, Windows, and Linux.
Recently, I noticed that .NET 9 added support for empty environment variables. This was particulary relevant due to a test failure of the new Dotenv implementation on Windows. It turns out that we inherited the same issue that earlier .NET versions had, which is an inability to disambiguate an unset environment variable from one that was set to the empty string. This issue has now been fixed in the latest development version.
Before:
IN: scratchpad "FACTOR" os-env .
f
IN: scratchpad "" "FACTOR" set-os-env
IN: scratchpad "FACTOR" os-env .
f
After:
IN: scratchpad "FACTOR" os-env .
f
IN: scratchpad "" "FACTOR" set-os-env
IN: scratchpad "FACTOR" os-env .
""
IN: scratchpad "FACTOR" unset-os-env
IN: scratchpad "FACTOR" os-env .
f
There might be other cross-platform environment-related topics to investigate, such as an open issue to look into case-preserving but case-insensitive environment variables on Windows.
PRs welcome!
HiDPI is a name for high resolution displays, sometimes called retina displays. A long long time ago, I added support for Retina Displays on macOS using Factor. But, they have not been well supported on either Linux or Windows platforms.
That ends today!
Some users have seen the “small window” problem on Linux, where on high resolution displays the Factor UI listener was rendered super tiny:
This is now fixed, it renders at the appropriate resolution detecting the
screen it is launched on, or using the GDK_SCALE
environment variable:
There has been one report that this works in the Gnome environments but not on KDE, so we might still have a few code changes necessary to make this more universal. And we also still need to switch from using our older GTK integration to the newer one with clean support for Wayland.
Other users have noticed the blurry text on Windows, due to using a legacy compatibility mode:
This is now fixed, rendering with the correct scaling factor:
It has been tested with 200% and 300% scaling factors. It is possible that intermediate scaling factors like 150% are not well supported and additional tweaks might be necessary to make this more universal.
Currently, on all three supported platforms, we use a global scaling factor which does not allow for moving Factor windows cleanly between screens with different scaling factors, for example when using HDMI on presentations, etc.
PRs welcome!
Neovim is a modern implementation of a vim-like editor. It started as a refactor, but “not a rewrite but a continuation and extension of Vim”. It does have some ability to load plugins built in Vimscript, but most new plugins seem to be written using the Lua programming language.
Factor has many different editor integrations supporting various text editors as well as plugins for some that provide additional features. One of these is the factor.vim plugin for Vim, which I happen to use frequently.
In the Big Omarchy 2.0 Tour, DHH presents about the Omarchy customization of Linux. I noticed that they have a pretty nice Neovim integration, particularly with the system themes. It turns out to be based somewhat on the Lazyvim system.
In any event, I wondered about how easy it would be to make a Neovim plugin for Factor. It isn’t fully necessary as there is support for Vimscript plugins and the Factor one works pretty well out of the box. However, I thought I’d ask Claude Code to go off and YOLO an implementation based on the existing one. Thankfully this was not a FAFO moment, and after a few cycles it came back with something that mostly works!
This is available in the factor.nvim repository and should be pretty easy to integrate into your Neovim setup. Perhaps give it a try and see what you think? I’ve been using it and it seems to work pretty well.
Encouraged by a fun rant about MacOS Tahoe’s Dead-Canary Utility App Icons, the reality that Apple is moving into the wonderful squircle-filled future, and the particularly annoying fact that legacy icons look terrible on macOS Tahoe – we have a new icon for Factor!
The latest development version includes new icon files in both PNG and SVG formats. And these are being used across macOS, Windows, and Linux builds. And, it might be burying the lede, but this is a particularly good time to do this as we finally have high-resolution “HiDPI” support working on Windows and Linux.
The next release is likely to be a good one!
The TA-Lib is a C project that supports adding “technical analysis to your own financial market trading applications”. It was originally created in 2001 and is well-tested, recently released, and popular:
200 indicators such as ADX, MACD, RSI, Stochastic, Bollinger Bands etc… See complete list…
Candlestick patterns recognition
Core written in C/C++ with API also available for Python.
Open-Source (BSD License). Can be freely integrated in your own open-source or commercial applications.
Of course, I wanted to be able to call the library using Factor. We have a C library interface that makes it pretty easy to interface with C libraries.
First, we add the library we expect to load:
<< "ta-lib" {
{ [ os windows? ] [ "libta-lib.dll" ] }
{ [ os macos? ] [ "libta-lib.dylib" ] }
{ [ os unix? ] [ "libta-lib.so" ] }
} cond cdecl add-library >>
LIBRARY: ta-lib
Then, we can define some types and some library functions to calculate the relative strength index:
TYPEDEF: int TA_RetCode
FUNCTION: TA_RetCode TA_RSI ( int startIdx, int endIdx, double* inReal, int optInTimePeriod, int* outBegIdx, int* outNBElement, double* outReal )
FUNCTION: int TA_RSI_Lookback ( int optInTimePeriod )
We use a simple code generator to define all the functions, as well as wrappers that can be used to call it:
:: RSI ( real timeperiod -- real )
0 int <ref> :> outbegidx
0 int <ref> :> outnbelement
real check-array :> inreal
inreal length :> len
inreal check-begidx1 :> begidx
len 1 - begidx - :> endidx
timeperiod TA_RSI_Lookback begidx + :> lookback
len lookback make-double-array :> outreal
0 endidx inreal begidx tail-slice timeperiod outbegidx outnbelement outreal lookback tail-slice TA_RSI ta-check-success
outreal ;
And, now we can use it!
IN: scratchpad 10 10 randoms 3 RSI .
double-array{
0/0.
0/0.
0/0.
50.0
62.16216216216216
31.506849315068497
46.38069705093834
32.33644859813084
59.75541967759867
66.53570603189276
}
You’ll note that the first few values are 0/0.
which represents a
NaN when we don’t have enough data to
compute an answer – either because we are in the lookback phase or because the
inputs have NaNs.
For convenience, we convert the inputs to double-array
to perform the
calculation, but if the input is already a double-array
then there is not
any data conversion cost.
There are some advanced techniques including use of the Abstract API for meta-programming, default values for parameters, candlestick settings, streaming indicator support, and documentation that we probably should think about adding as well.
This is available on my GitHub.
I was reminded recently about a great article about unicode string lengths:
It’s Not Wrong that
"🤦🏼♂️".length == 7
But It’s Better that
"🤦🏼♂️".len() == 17
and Rather Useless thatlen("🤦🏼♂️") == 5
This comes at a time of excessive emoji tsunami thanks to the proliferation of large language models and probably lots of Gen Z in the training data sets. Sometimes emojis are fun and useful like in Base256Emoji and sometimes it can get carried away like in the Emoji Kitchen.
I have written about Factor’s unicode support before and wanted to use this example to show a bit more about how Factor represents text using the Unicode standard.
IN: scratchpad "🤦" length .
1
IN: scratchpad "🤦🏼♂️" length .
5
Wat.
Well, what is happening is that the current strings vocabulary stores Unicode code points. This can be both useful and useless depending on the task at hand. We can print out which ones are used in this example:
IN: scratchpad "🤦🏼♂️" [ char>name . ] each
"face-palm"
"emoji-modifier-fitzpatrick-type-3"
"zero-width-joiner"
"male-sign"
"variation-selector-16"
When a developer expresses a need to store or retrieve textual data, they likely need to know about character encodings. In this case, we can see the number of bytes required to store this string in different encodings:
IN: scratchpad "🤦🏼♂️" utf8 encode length .
17
IN: scratchpad "🤦🏼♂️" utf16 encode length .
16
IN: scratchpad "🤦🏼♂️" utf32 encode length .
24
But, what if we just want to know how many visual characters are in the string?
IN: scratchpad "🤦🏼♂️" >graphemes length .
1
This is covered in The Absolute Minimum Every Software Developer Must Know About Unicode in 2023, which is also a great article and covers this as well as a number of other aspects of the Unicode standard.
planet-factor is an Atom/RSS aggregator that collects the contents of Factor-related blogs. It is inspired by Planet Lisp.