看了GB国家先行标准中信息技术的部分,能感受到后续做工业软件的套路:把自己做事情的方式和要求作为标准,但好像没有特别介绍清楚这么做带来的好处。
看到了健康码的标准,BAT三家公司都参与了。还有Linux系统的测试方法标准。感觉诸如工艺设计平台,面向图形、图像、文字的编辑与排版功能都可以申请标准。还有印花硬件的测试等也都可以试试,
$LAYYYTER

⁂

★
🪼

pixel skylines
YOU ARE THE REASON
almost home
No title available
Sweet Seals For You, Always
h
i don't do bad sauce passes
One Nice Bug Per Day
Monterey Bay Aquarium
hello vonnie
sheepfilms

祝日 / Permanent Vacation

blake kathryn

if i look back, i am lost
Today's Document
2025 on Tumblr: Trends That Defined the Year
seen from United States
seen from United Kingdom
seen from United Kingdom
seen from United States

seen from Brazil

seen from United States

seen from United Kingdom

seen from Malaysia

seen from Italy

seen from United States
seen from Canada
seen from Malaysia

seen from Morocco

seen from United States

seen from Malaysia
seen from United States

seen from Germany
seen from Spain
seen from T1

seen from Malaysia
@redraiment
看了GB国家先行标准中信息技术的部分,能感受到后续做工业软件的套路:把自己做事情的方式和要求作为标准,但好像没有特别介绍清楚这么做带来的好处。
看到了健康码的标准,BAT三家公司都参与了。还有Linux系统的测试方法标准。感觉诸如工艺设计平台,面向图形、图像、文字的编辑与排版功能都可以申请标准。还有印花硬件的测试等也都可以试试,
关于OKR的思考
因为公司最近也要开始推OKR了,取代原来的KPI,今天正好看到一篇文章在讨论这个问题:《OKR救不了互联网打工人》。
看完之后我的理解是:KPI定的是把事情做到什么程度;OKR定的是做成什么事情。
比如,我希望把Workstation做成行业内通用的目标设定(交付物)、流程编排、任务执行等系统,这是想做成的事情;我希望Workstation在有100家公司来用,这是做到什么程度。
这个差异是:数字化的指标通常是短视的,短期内达成的指标对于长期而言并没有真正的好处。比如是否决定做某次大促,站在KPI的角度,能完成短期内GMV等数字指标,所以必须要做;而站在OKR的角度,做完大促后,对于搭建电商交易平台、卖家与买家长期留存这个目标并没有实质帮助,所以不应该做。
React解决什么问题?
查找React解决什么问题比Redux解决什么问题会简单一些,Google出来的答案,基本上能从最初遇到的问题说起。
问题背景是Facebook的前端应用,会随着时间变化而需要处理海量数据,导致大量的DOM操作,而引起性能问题。因为用JS操作DOM,是需要从JS引擎切换到渲染引擎,就像操作系统从用户态切换到内核态一样,是非常耗时的操作。
React通过Virtual DOM,在JS引擎内完成DOM的差异比较,只有真正的差异点才需要重新渲染,提升DOM操作的性能。
Redux解决什么问题?
在设计Workstation时,想解决当前意大利面条式的任务状态管理代码,隐隐觉得可以通过Redux的Unidirectional Data Flow概念来解决。但动手写设计思路时却感觉无法说服自己为什么要引入这样的概念。
究其原因,是自己尚不能清晰地、自信地回答为什么需要Redux,即Redux解决了什么问题。所以停下手上工作,先去寻找答案。
Google了大半天,看了包括官方文档、StackOverflow、Medium等各种资料,最终觉得还是《What Does Redux Do? (and when should you use it?)》这篇介绍得最贴切:
几乎所有关于Redux的文章(包括Redux首页),都在强调Redux是可预测的状态容器(Predictable State Container),因此他们都会重点介绍Redux如何做状态管理。但我很困惑,React不是已经有状态管理机制了吗(setState),并且一个状态对应一种页面渲染结果就是React提出的,搞得好像没有Redux之前,React就没有状态管理一样。在这篇文章里,也表达了相同的观点:Redux虽然让状态管理变得更好,但Redux的解决的问题并不是状态管理。
Redux真正解决的是数据流的问题:跨层级传递数据。解法是将数据的管理独立出来,统一由Store完成,实现将任何数据插入任何组件(Plug Any Data into Any Component)的功能。
Redux的机制其实和Excel的formula很像!假如A3=A1+A2,当A1或A2发生变化时,就自动触发依赖的公式重新计算,也是一种按需计算的方式。
类比到Workstation,Workstation与传统的任务流程引擎的差异正式层级立体结构的任务体系,任务的状态或上下文数据需要在不同层级的流程间共享,因此Redux的理念是正好适合这样的复杂场景。
完备性思考(三)
完备性之后是便捷性。
首先是最精简但完备的功能内核,在此基础上再提供常见(20%)常见的便捷接口。
完备性思考(二)
我想用数学的语言来解释一下我对系统功能完备性的理解。
抽象代数中对群的要求是:任何一个群在其所定义的两种运算之下,都是封闭的(closed)。这意味着,群中的任何两个元素进行群运算,其运算结果仍然位于该群中,而且任何一个群元素的逆元素,也同样位于该群之内。
用群的定义做类比,判断一个系统的功能是否完备,可以将系统的状态定义为群元素,系统的功能定义为群运算,若通过群运算能产生出所有的群元素,则群运算的功能是完备的。
拿中国象棋举例:棋盘上棋子的任意一种摆放状态就是一个群元素,群运算就是将某种状态迁移到下一个状态(符合棋子走法),判断象棋系统的功能是否完备,可以看这些功能能否产生象棋的所有种摆放状态。
对完备性的追求
中午和圣国聊天,提到我的性格比较偏学院派,喜欢追求完备性。的确,我每做一件事情都喜欢了解完整的体系,并提炼出最小的、完备的内核,再通过这个内核发散出各种场景。
这样追求可能在其他人眼里是偏执,但这恰恰是我持续保持激情的原因。就像任贤齐版的神雕侠侣里,李立群饰演的欧阳锋总结武学的意义是让人不要停止追求,这种偏执也让我无法停止追求更完美的解法。
我是典型的先验主义者,崇拜像特斯拉这样的、光靠想象就能完成实验的,我追求完备的、统一的方案,就像物理学家不断追求相对论与量子物理的结合一样。
通过git自动发布
希望测试服务器端通过GIT HOOK自动部署代码的功能。思路如下:
在服务器上创建GIT工程。
用ssh的方式克隆到本地。
配置服务器端的HOOK:在收到push后,并且分支是master时,则自动重启服务或热部署。
本地开发,然后merge到master分支,最后push到远程。
操作记录
一、克隆本地项目
结论是可以克隆,但不能push,因为提示不能修改已经checkout的分支。所以只能创建纯仓库。解法如下:
git init --bare repository git clone $PWD/repository service git clone $PWD/repository client
二、开发服务器端钩子
根据需求,需要实现的post-receive挂钩。
但为了实现只有master分支才部署,先输入所有环境变量和标准输入的内容看看有哪些信息可以利用。
标准输入的内容是三个参数:
oldrev
newrev
refname
其中refname会指向refs/heads/master,通过命令git rev-parse --symbolic --abbrev-ref $refname可获得分支的命令。
现在可以试试直接到目标目录下执行git pull,拉去最新的代码。
自动pull失败,输出命令看看出什么问题。
原来是路径写错了,修正后再看看~
路径对了,但git执行失败,返回错误码128,将命令结果重定向一下看看。
无法重定向,用子命令看看。
先查看hook命令的当前路径是什么。
输出$0和dirname $0。
通过调用外部命令来更新。
通过先cd到目标目录来执行更新。
通过修改环境变量GIT_DIR。
指定git -C <dir>再试一次。
参考了Stack Overflow的帖子:https://stackoverflow.com/questions/4043609/getting-fatal-not-a-git-repository-when-using-post-update-hook-to-execut ,改用env -i试试。
终于成功了,现在试试隐藏远程的输出信息。
改用子命令的重定向。
三、服务端部署
希望Nginx的目录里没有.git目录
没想到一把成功!解法是:
git clone --separate-git-dir service.git $PWD/repository service`,将`.git git -C service config core.worktree $PWD/service rm service/.git
总结
完整的流程记录如下:
服务端创建仓库
git init --bare repos/fe.git # 前端仓库 git init --bare repos/be.git # 后端仓库 git clone --separate-git-dir /var/www/lambdata.cn $PWD/repos/fe.git repos/lambdata.cn.git git -C repos/lambdata.cn.git config core.worktree /var/www/lambdata.cn rm /var/www/lambdata.cn/.git
服务端注册钩子
```repos/fe.git/hooks/post-receive
!/bin/sh
repos=/home/redraiment/repos workspace_home=$repos/lambdata.cn.git
while read oldrev newrev refname; do branch=$(git rev-parse --symbolic --abbrev-ref "$refname") if [ "master" = "$branch" ]; then env -i git -C "$workspace_home" pull break fi done
## 本地端开发代码 ```sh git clone ssh://[email protected]:/home/redraiment/repos/fe.git lambdata.cn cd lambdata.cn ... git push
重启日历记
用了几天Tumblr之后,它的编辑器还是很多坑。比如:
必须先把文字敲出来,才能选中变成列表项等格式。
中文输入标题时,第一个总是出错(默认会输入英文字母)。
Markdown的代码最终展示出来的效果会去掉行号。这个非常要命。
富文本编辑器的列表无法缩进:这个真是郁闷。
坚持了几天,还是打算重启原来的日历记项目。但是创作过程可以记录在Tumblr。目标:
自己记录文字、图片或视频。
可按照日历、列表等的多种方式来组织。
提供强大的整理功能。
提供强大的搜索功能。
支持Markdown。
首先是技术选型,还是自己喜欢的组合:FreeBSD+PostgreSQL+Clojure+Nginx+Supervisor+React。
Haskell学习
之前看Haskell相关的文章时,总是各种吹捧其类型系统如何强大,而且是一谈Haskell,立刻聊到函数式编程,立刻聊到Monad,立刻聊到范畴论,立刻聊到数学。
趁今年春节没出远门,就在Kindle上买了一本《Haskell趣学指南》,学一点电(工)视(作)上不让播(用)的冷门知识。而且这回学习的目标也非常明确——解答“Haskell的类型系统与语言的差别”与“Monad解决什么问题”两个疑惑。陆陆续续看完了教程,也查看了一些相关资料,心中已经有了基本的答案,因此记录下来。为了方便以后自己理解和回忆,所有Haskell的概念与代码都会尽量提供等价或相近的Java概念与代码。
类型系统
关于类型系统的差异,知乎上有个非常好的问题《Haskell的类型类是否遵循里式替换原则?》。文中提到一个例子:
> :type fromIntegral fromIntegral :: (Integral a, Num b) => a -> b > :type sqrt sqrt :: Floating a => a -> a > :type sqrt . fromIntegral sqrt . fromIntegral :: (Floating c, Integral a) => a -> c
上述三行代码分别返回三个函数的出入参类型:
fromIntegral函数:入参a是Integral,出参b是Num。
sqrt函数:入参与出参都是Floating。
组合函数sqrt . fromIntegral:等价于将fromIntegral的输出作为sqrt的输入,入参a是Integral,出参c是Floating。
为方便理解,Haskell的Integral类型可以看成Integer,Num可以看成Number,Floating可以看成Float。在Haskell中,Num可以理解为Integral与Floating公共基类,Java中Number同样也是Integer与Float的基类。因此,上述三个函数等价的Java方法定义如下:
Number fromIntegral(Integer a); Float sqrt(Float a);
在Java中调用sqrt(fromIntegral(...))会直接报编译错误,因为Number是Float的基类,无法作为sqrt的入参;但Haskell代码却可以正常执行!
类型类
究其原因,是因为Num并不是类型(class),而是类型类(type class)。类型类可以看成是类型的约束,Java中类似的概念是“接口+泛型”。因此fromIntegral不能读作“返回Num类型的函数”,而应该读作“返回任何一个符合Num约束的类型的函数”。Java较相近的写法如下:
T fromIntegral(Integer a);
至于函数具体返回哪一个符合Num约束的类型,在定义fromIntegral的时候并不知道,Haskell编译器会通过类型推导功能,在每一处函数调用的地方,动态地算出真正的类型。
上述例子中,sqrt的入参Floating实际上也是类型类,因此组合sqrt与fromIntegral时,类型系统会要求fromIntegral的返回值类型既要满足Num的约束也要满足Floating的约束,由于Floating就是Num的子类型类,因此fromIntegral函数的返回值约束动态地变成了满足Floating约束。
面向接口编程
其中里氏替换原则(Liskov Substitution Principle)是指继承必须确保超类所拥有的性质在子类中仍然成立,即子类可以扩展父类的功能,但不能改变父类原有的功能。
Monad
关于账户的思考
这么多年的工作经验告诉我,账户体系是一家公司最重要的基础设施,因为账户体系决定了整个生态的参与者!因此账户中心是否涉及良好,直接决定了业务发展的天花板。
假设某产品一开始只面向个人用户,若账户体系只面向个人用户设计,则未来用户无法以企业用户的身份来使用。
因此,如果我还有机会真正从从零开始发展一个新业务,账户体系是首要抓的事情!在账户体系完善之前,其他的工作都应该往后放一放。一旦账户体系建设完善(包括账户管理、登入管理、权限管理、会话管理等核心模块),其他的产品都围绕账户中心建设,对于用户而言,至少整个生态是打通的(一卡通)。
Applicative推导
在看《Haskell趣学指南》时,其中有一个Applicative非常有意思:
let x = (+) <$> (*3) <*> (*5) in x 10
这段代码的执行结果是80,即3 * 10 + 5 * 10 = 80。为了搞清楚这个函数组合的过程,我去找了一下函数的定义:https://hackage.haskell.org/package/base-4.14.1.0/docs/src/GHC.Base.html#line-973
instance Applicative ((->) r) where pure = const (<*>) f g x = f x (g x)
根据这个定义,来逐步推导一下原始公式:
(+) <$> (*3) <*> (*5) -- => pure (+) <*> (*3) <*> (*5) -- => const (+) <*> (*3) <*> (*5)
根据<*>的定义,此时参数分别是:
f:const (+)
g:(*3)
x:待输入
因此,上述表达式可变换为:
(\x -> const (+) x ((*3) x)) <*> (*5) -- => (\x -> (+ ((*3) x))) <*> (*5) -- => (\x -> (+) ((*3) x)) <*> (*5)
同理,此时<*>的参数分别是:
f:\x -> (+) ((*3) x)
g:(*5)
x':待输入
上述表达式再次变换为:
\x' -> (\x -> (+) ((*3) x)) x' ((*5) x') -- => \x' -> (+) ((*3) x') (*5 x')
得到最终的表达式\x' -> 3 * x' + 5 * x'
code less的思考
最近code less的概念很火,但其中滥竽充数的很多,大多数code less的概念都只是“减少文本形式的代码”。实际上,code从定义上只是计算机执行的指令,并没有严格地要求必须是文本形式。很多打着code less口号的系统,实际上做的只是形式的转换,把文本型的代码转换成鼠标拖拽的可视化流程图,一加一减等于0,甚至因为鼠标操作的不精确性,反而大概率会导致效率不升反降。
Embedded PostgreSQL
今天继续开发Embedded-PostgreSQL,从春节前开始:
最初打算基于ru.yandex.qatools.embed:postgresql-embedded做包装,发现Hard Code得太多,最新的PG 13都跑不起来,因为项目已经不维护了,只能自己修改源码。
修改源码就等于基于de.flapdoodle.embed.process 这个底层框架自己实现PostgreSQL的进程管理,开发了几天,今天实在忍受不了这套框架,设计得即不灵活又不优雅...所以今天下午决定抛弃他们完全从零开始做。
刚刚开发并测试完成,发现只要790行代码
重复造轮子是非常快乐的事情!
吐槽de.flapdoodle.embed.process
de.flapdoodle.embed.process 的接口是在太不友好了!封装的过程有大量的信息丢失,同时类的组织混乱、毫无体系。比如:
RuntimeConfig作为运行时的配置,还会包含存储、下载、解压等不相关的配置。
解压的FileSet里有且只能有一个可执行程序,诸如PostgreSQL这种包含多个可执行程序的完全无法适配。
AbstractProcess里的pidFile居然指向的是可执行文件。
Executable和AbstractProcess里重复地检查是否是Daemon进程,并添加JobKiller
Starter已经在控制整个进程的启动了,结果在AbstractProcess里还冗余地处理进程的PreProcess和PostProcess。
……
Paul Graham的《黑客与画家》虽然家喻户晓,但对于技术同学而已,只不过是一本鸡汤而已;反而作者在1994年写的《On Lisp》才充满了技术的真知灼见,其中的Bottom-up Design,每回看都有不一样的体会~
变得不想“交流”
随着年龄增长,慢慢变得不想“交流”。之所以带引号,是因为不想做低效的“交流”。现在工作和学习过程中充满了低效的交流,需要花很大力气和对方对齐自己的认知。这真的很浪费自己的时间和精力,尤其是面对那群已经思维固化的老油条,他们本身已经没有动力学习新的知识、了解新的方式,想和他们对等交流还得苦口婆心让他们先了解很多背景知识。
以前我还有耐性向他们讲解。一方面是想装逼,让其他人知道我有NB 的想法;另一方面也是为了让事情推动下去,不得不耐着性子向他们解释前因后果。很多时候,他们一知半解后,还抢着说这是自己想出来的,然后开始到处宣传,真是无知者无畏。
现在年龄不断增长,想装逼的心态没以前那么强烈了,所以对SB们的容忍度也减弱了,实在不想再浪费自己的时间和精力让向他们解释。
所以,后续还是专注在自己的成长上,把事情做出来为主,不要想着让其他人能理解。因为和SB们交流,他们会先把你拉到和他同一水平线,再用经验打败你。