Aeson Parsing Tips

Array with Objects

Suppose we have the JSON data:

1
2
3
4
5
6
7
8
9
10
{
"media_id": "981538",
"tags": [ {
"id": 12227,
"name": "english"
}, {
"id": 58971,
"name": "japanese"
} ]
}

We want it to be parsed into the following structure, with the r_tags taking only the name field of tags.

1
2
3
4
data Result = Result {
r_id :: Text
, r_tags :: [Text]
}

The easiest way is to parse via newtype wrapper:

1
2
3
4
5
6
7
8
9
10
11
12
13
data Result = Result {
r_media_id :: Text
, r_tags :: [RTag]
}
instance FromJSON Result where
parseJSON = withObject "Result" $ \v -> Result
<$> (v .: "media_id")
<*> (v .: "tags")

newtype RTag = RTag { tag :: Text }
instance FromJSON RTag where
parseJSON = withObject "RTag" $ \v ->
RTag <$> ((v .: "name"))

But we can simplify this by removing the RTag wrapper. But first let’s take a look at some Aeson types:

1
2
3
4
type Array = Vector Value
type Object = HashMap Text Value
parseJSON :: Value -> Parser a
(.:) :: FromJSON a => Object -> Text -> Parser a

And we can infer that (v .: "tags") parse tags into an Array(so has type Parser Array), inside which elements are parsed by the instance of RTag. Obviously, if we want to parse the tags directly into [Text], we need to turn the type of (v .: "tags"), Parser Array, into Parser [Text].

It can be done with a do block, and a little help from toList:

1
2
3
4
5
6
7
8
9
import Data.Foldable (toList)
import Data.Aeson.Types (Parser)

parseJSON = withObject "Result" $ \v -> Result
<$> (v .: "media_id")
<*> do
tagArray <- (v .: "tags") :: Parser Array
tagObjs <- traverse (parseJSON :: Value -> Parser Object) $ toList tagArray :: Parser [Object]
traverse (.: "name") tagObjs

Then remove do block:

1
(v .: "tags") :: Parser Array >>= traverse parseJSON . toList >>= traverse (.: "name")

Field with different types

1
2
3
4
5
6
7
"result": [ {
"id": 1531017
"media_id": "148011"
}, {
"id": "5137523"
"media_id": "135611"
} ]

Look at the id field above, did you find that it can be either a String or Int? It can be parsed with a parseId function.

1
2
3
4
parseId :: Value -> Parser Text
parseId (Number o) = pure $ (fst . Text.breakOn ".") $ Text.pack $ show o
parseId (String o) = pure o
parseId _ = mzero

With structure:

1
2
3
4
5
6
7
8
data Result = Result {
r_wtf_id :: Text.Text
, r_media_id :: Text.Text
}
instance FromJSON Result where
parseJSON = withObject "Result" $ \v -> Result
<$> ((v .: "id") >>= parseId)
<*> (v .: "media_id")

用例子理解批处理脚本

我们来看看如何将目录下的每一个文件加上原目录名的前缀:

1
2
3
4
5
6
7
for /f "delims=" %%i in ('dir /ad/b') do (
:: 遍历子目录
for /f "delims=" %%j in ('dir /b %%i') do (
:: 遍历子目录下文件
echo %%j | findstr "^%%i" && (echo No need to rename.) || (move "%%i\%%j" "%%i\%%i-%%j")
)
)

解释

看看上面的脚本中的这一句:

for 循环

1
for /f "delims=" %%i in ('dir /ad/b') do (

此处for语句的结构是 FOR %variable IN (set) DO command [command-parameters] 。参数的说明如下:

  • %variable: 在遍历中每个(set)中文件的替代符号(只能使用一个字母,否则多余部分会被当成字符串处理)
  • (set): 表示一个或多个文件 (括号不能省去)
  • command: 对于每一个(set)中的文件所要执行的操作
  • [command-parameters]: 可选的,上述command所要带的参数

当你在脚本文件中声明(%variable)时,请使用%%i而非%i。并注意变量名是区分大小写的(如%%i%%I是两个不同的替代符)。你可以用Win+R打开cmd,在其中输入语句+空格+/?(如for /?)查询帮助文档。

但是你可能还发现在for的后面还跟着一串/f "delims="。这里/f是一个指令拓展(此外有/d,/r,/l三种),当你使用/f时,你可以以如下的结构来使用 for 循环:

1
2
3
FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ('string') DO command [command-parameters]
FOR /F ["options"] %variable IN (`command`) DO command [command-parameters]

上面的语句使用 "delims=" ,其目的是将_定界符_设为空,以防止文件名被默认的定界符(空格)分开。我们使用 ('dir /ad/b') 作为 (set)dir 的用处是获取当前目录及子目录内所有的文件,其选项 /ad 代表我们只需要文件夹, /b 则意味者我们只需要获取文件(夹)名。你可以在cmd中键入 dir /? 查看详细。

执行命令

接下来把注意力放在 do 之后的这一行语句:

1
echo %%j | findstr "^%%i" && (echo No need to rename.) || (move "%%i\%%j" "%%i\%%i-%%j")

它实际上是多条命令组合在一起的。我们来看看一些特殊符号的作用:

  • &&: 按顺序执行前后两条命令,当前一条命令出错时将不执行后面的命令
  • ||: 按顺序执行前后两条命令,当前一条命令正确执行后将不执行后面的命令
  • |: 将前一命令的输出作为后一指令的输入

此外还有一些别的符号:

  • &: 按顺序执行多条命令,不论命令是否执行成功
  • >: 将前一指令的输出覆盖写入后面的文件
  • >>: 将前一指令的输出追加写入后面的文件

所以我们可以知道上面的那一行语句的意思大概是:
echo%%j 的内容通过 | 传递给 findstr "^%%i" ,并且如果 findstr%%j 中能够找到以 %%i 开头的内容,那么用 echo 在屏幕上打印"No need to rename."的辅助信息,否则将位于 "%%i 目录下的文件 %%j 移动到 %%i 目录下并重命名为%%i-%%j

非常简单。

一些细节

让我们来看看每一条命令的详细:

  • echo [message]: 将 [message] 输出
  • findstr strings: 查找字符串 strings (支持正则表达式符号 .*^$等等 ,使用 findstr /? 查看详细)
  • move filename destination: 移动并重命名文件或文件夹

对于 %%j%%i ,我们知道,它是 (set) 中每一元素的代替符号,意思是说,我们对 %%j ( %%i )进行任何操作,就相当于对其所指代的(set)的每一个元素进行同样的操作。

举个例子,我们写出一个for循环:for /f "delims=" %%i in ('dir /ad/b') do move %%i %%iCopy中,我们用dir /ad/b得到以下输出(每一行是文件夹的相对路径)

1
2
文件夹一
文件夹二

那么 文件夹一文件夹二 会分别被代入到 %%i 中,执行相应的命令(这里是move %%i %%iCopy):

1
2
move 文件夹一 文件夹一Copy
move 文件夹二 文件夹二Copy

上面的两条命令都会被执行,结果是 文件夹一 被重命名为 文件夹一Copy文件夹二 被重命名为 文件夹二Copy

参考资料

致谢

感谢 @丶慕忻 提供的想法,本文中的例子出自于他。

Compiling Statically with glibc Raises segfault at Runtime

I’m writing a chatting bot with Haskell and I need to statically compile my program, run it on the server which is running on Ubuntu 18.04 bionic. When I built my stack project with GHC option -optl-static, running it, I got a segfault. Checking the core dump file and I got this:

1
2
3
4
5
6
Stack trace of thread 26051:
#0 0x00007f5ce4cef448 n/a (/lib/x86_64-linux-gnu/libnss_files-2.27.so)
#1 0x00007f5ce4cf08bd _nss_files_gethostbyname4_r (/lib/x86_64-linux-gnu/libnss_files-2.27.so)
#2 0x0000000001a41d66 n/a (/home/nutr1t07/wl)
#3 0x0000000001a42c39 n/a (/home/nutr1t07/wl)
#4 0x0000000000ddcf91 n/a (/home/nutr1t07/wl)

I ran the same program on my PC, it raised a segfault as well. The core dump info was as follow:

1
2
3
4
5
6
7
8
Stack trace of thread 38504:
#0 0x00007f6c7165ef43 n/a (/usr/lib/libnss_resolve.so.2 + 0x6f43)
#1 0x00007f6c7166b47e n/a (/usr/lib/libnss_resolve.so.2 + 0x1347e)
#2 0x00007f6c7168a119 n/a (/usr/lib/libnss_resolve.so.2 + 0x32119)
#3 0x00007f6c7168a3a5 n/a (/usr/lib/libnss_resolve.so.2 + 0x323a5)
#4 0x00007f6c7168a976 n/a (/usr/lib/libnss_resolve.so.2 + 0x32976)
#5 0x00007f6c7168e7e7 _nss_resolve_gethostbyname4_r (/usr/lib/libnss_resolve.so.2 + 0x367e7)
#6 0x000000000197dd96 n/a (/home/nutr1t07/wl-bot/.stack-work/install/x86_64-linux-tinfo6/7c9765861f2b111532edba40b3a00a85ac41e39dc6945f6238a49b1465764ba9/8.6.5/bin/wl-bot-exe + 0x157dd96)

NSS (Name Service Switch)

29.2.1 Services in the NSS configuration File (from glibc manual):
"Assume the service name shall be used for a lookup. The code for this service is implemented in a module called libnss_name. On a system supporting shared libraries this is in fact a shared library with the name (for example) libnss_name.so.2."

So we can conjecture that there should be something wrong with NSS.

The Name Service Switch (NSS) is a facility that provides a variety of sources for common configuration databases and name resolution mechanisms.[1] Here is what a part of its configuration file(/etc/nsswitch.conf) looks like:

1
2
3
4
5
6
7
8
passwd: files mymachines systemd
group: files mymachines systemd
shadow: files

publickey: files

hosts: files mymachines myhostname resolve [!UNAVAIL=return] dns
networks: files

Its configuration is of the pattern database: <service>. NSS would attempt to use the services in order of them to resolve queries on the specified database. Check this manual for more details.

glibc

glibc uses dlopen(3), which is used to load dynamic shared objects, to load NSS modules. This requires at runtime the shared libraries from the glibc version used for linking, which makes the program need a second copy of the C library. It means the statically linked program has two copies of the C library in its address space, and they might fight over whose stdout buffer is to be used, who gets to call sbrk with a nonzero argument, that sort of thing.[2]

There is also a FAQ on glibc wiki that explained why statically linked programs need shared libraries.

There must be some advantages for doing so. And the mechanism of NSS is also a feature of glibc. I have nothing to say about that.

Any Solutions?

However, NSS doesn’t seem to be very necessary for me. Another libc musl which does not have NSS[3] is likely an alternative of glibc. A docker image ghc-musl provides GHC compiled with musl, which can be easily used on stack projects.

Or use an awesome project static-haskell-nix to build static binaries with Nix.

Acknowledgements

Haskell-Cafe


  1. Name Service Switch

  2. Why is statically linking glibc discouraged?

  3. Future Ideas of musl

HIE in nvim on Archlinux

HIE(Haskell IDE Engine) is an engine for IDE/Editor integration and was first released on May 3, 2018. Before HIE, there was a project for Haskell programming enrichment which named ghc-mod. But that one was deprecated on Jan 25, 2019.[1]

As a modern integration tool for Haskell, HIE comes with a great number of features:

  • Uses LSP, so should be easy to integrate with a wide selection of editors
  • Diagnostics via hlint and GHC warnings/errors
  • Code actions and quick fixes via apply-refact
  • Show type information and documentation(via haddock) on hover
  • List all top-level definitions
  • Automatic completion
  • Formatting via brittany
  • Renaming via HaRe
  • Typo quick fixes
  • Add missing imports (via hsimport)

In this article, I’ll show you how to use HIE with ALE(Asynchronous Lint Engine) or coc.nvim in nvim.

HIE

Please build HIE from source instead of installing it from AUR. If you really do that, you may get troubled with problems like the missing of cabal-helper-wrapper.

Build from source

Download the source code

1
2
git clone https://github.com/haskell/haskell-ide-engine --recurse-submodules
cd haskell-ide-engine

Build

Download a Stackage-LTS and an appropriate GHC.
1
stack ./install.hs help
Install specific GHC Version

Install HIE for GHC version 8.6.5 and get the required data-files for HIE (Hoogle DB)

1
2
stack ./install.hs hie-8.6.5
stack ./install.hs build-data

1. coc.nvim

Install

Use Vim-Plug to install the plugin.
Add the following content to init.vim:

1
2
3
call plug#begin()
Plug 'neoclide/coc.nvim', {'branch': 'release'}
call plug#end()

Then restart nvim and run :PlugInstall.

Configuration

Run :CocConfig in nvim and add the following to your coc config file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"languageserver": {
"haskell": {
"command": "hie-wrapper",
"rootPatterns": [
".stack.yaml",
"cabal.config",
"package.yaml"
],
"filetypes": [
"hs",
"lhs",
"haskell"
],
"initializationOptions": {
"languageServerHaskell": {
}
}
}
}
}

Restart nvim.

2. ALE

Install ALE and LanguageClient-neovim

Also using Vim-Plug to install the two plugins:

1
2
3
4
5
Plug 'dense-analysis/ale'
Plug 'autozimu/LanguageClient-neovim', {
\ 'branch': 'next',
\ 'do': './install.sh'
\ }

Restart nvim and run :PlugInstall.

Configuration

Add this to nvim config file(~/.config/nvim/init.vim):

1
let g:LanguageClient_serverCommands = { 'haskell': ['hie-wrapper', '--lsp'] }

Now the config(~/.config/nvim/init.vim) should include:

1
2
3
4
5
6
7
8
9
let g:LanguageClient_serverCommands = { 'haskell': ['hie-wrapper', '--lsp'] }

call plug#begin()
Plug 'dense-analysis/ale'
Plug 'autozimu/LanguageClient-neovim', {
\ 'branch': 'next',
\ 'do': './install.sh'
\ }
call plug#end()

Troubleshooting

[coc.nvim] Got error while processing diagnostics: readCreateProcess: cabal-helper-wrapper “print-build-platform” (exit 1): failed

While opening a .hs file that is not under a stack project directory, hie-wrapper(a tool for identifying hie with specified GHC version) will not use stack ghc, instead it fallbacks to system GHC. So if you run hie-wrapper under non-stack directory, an error will arise:

1
2
3
4
5
6
7
8
2019-11-03 09:56:40.464794427 [ThreadId 4] - run entered for hie-wrapper(hie-wrapper) Version 0.13.0.0, Git revision 38a6febbeeaaf627d932b0314584de16f9fe0a8b (3043 commits) x86_64 ghc-8.6.5
2019-11-03 09:56:40.468284219 [ThreadId 4] - Current directory:/home/nutr1t07
2019-11-03 09:56:40.469282644 [ThreadId 4] - Operating system:linux
2019-11-03 09:56:40.47165505 [ThreadId 4] - Cradle directory:/home/nutr1t07
2019-11-03 09:56:40.471983891 [ThreadId 4] - Using plain GHC version
2019-11-03 09:56:40.472241244 [ThreadId 4] - Project GHC version:No System GHC found
2019-11-03 09:56:40.472486567 [ThreadId 4] - hie exe candidates :["hie-No System GHC found","hie","hie"]
2019-11-03 09:56:40.472856203 [ThreadId 4] - cannot find any hie exe, looked for:hie-No System GHC found, hie, hie

And running under a stack project will not:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2019-11-03 09:57:59.150831371 [ThreadId 4] - run entered for hie-wrapper(hie-wrapper) Version 0.13.0.0, Git revision 38a6febbeeaaf627d932b0314584de16f9fe0a8b (3043 commits) x86_64 ghc-8.6.5
2019-11-03 09:57:59.154236705 [ThreadId 4] - Current directory:/home/nutr1t07/a_stack_project
2019-11-03 09:57:59.155404581 [ThreadId 4] - Operating system:linux
2019-11-03 09:57:59.488924051 [ThreadId 4] - Cradle directory:/home/nutr1t07/a_stack_project
2019-11-03 09:57:59.489251508 [ThreadId 4] - Using stack GHC version
2019-11-03 09:57:59.699420887 [ThreadId 4] - Project GHC version:8.6.5
2019-11-03 09:57:59.699677886 [ThreadId 4] - hie exe candidates :["hie-8.6.5","hie-8.6","hie"]
2019-11-03 09:57:59.70006366 [ThreadId 4] - found hie exe at:/usr/bin/hie-8.6.5
2019-11-03 09:57:59.700362808 [ThreadId 4] - args:[]
2019-11-03 09:57:59.700762353 [ThreadId 4] - launching ....



2019-11-03 09:57:59.702433004 [ThreadId 4] - Using stack GHC version
2019-11-03 09:57:59.910601176 [ThreadId 4] - Run entered for HIE(hie-8.6.5) Version 0.13.0.0, Git revision 38a6febbeeaaf627d932b0314584de16f9fe0a8b (3043 commits) x86_64 ghc-8.6.5
2019-11-03 09:57:59.911223928 [ThreadId 4] - Current directory:/home/nutr1t07/a_stack_project
2019-11-03 09:57:59.911482391 [ThreadId 4] - args:[]

So you may edit the file under a stack project directory. That will make HIE works properly.


  1. Officially deprecate ghc-mod (the development tool)

Set Proxy for vcpkg

vcpkg use CMake to perform the downloads for building libraries. This function is backed by curl, which doesn’t use Windows’ system-wide proxy settings.

If we want to perform these downloads under proxy, we need to specify some environment variables (http_proxy, https_proxy).

For shadowsocks-windows, the listening proxy port has been converted to HTTP proxy from SOCKS. Enter the following commands to cmd:

1
2
set http_proxy=http://127.0.0.1:1080
set https_proxy=http://127.0.0.1:1080

Check the environment variables using:

1
set

多巴胺与近视发展

多巴胺是视网膜中重要的神经递质,介导多种功能,包括视觉传递和屈光发育。[1]来自不同物种的几个实验数据表明,多巴胺起抑制屈光发育的作用。[2]该现象体现于灵长类动物、鸡和豚鼠形觉剥夺后带来的视网膜或玻璃体的多巴胺和/或3,4-二羟基苯乙酸(视网膜中多巴胺的主要代谢产物)水平降低。[3]

如果近视眼发育期间多巴胺水平降低和/或信号传导减少,则可预测增加多巴胺水平和多巴胺受体活性能预防近视。实验说明,多巴胺水平提升和使用多巴胺受体激动剂增加多巴胺信号传导,可防止几种动物的形觉剥夺性近视。[4]这些研究表明,在异常视觉条件(如形觉剥夺)下生长的常规屈光眼生长缺少多巴胺受体激活,且增加眼内多巴胺水平可以防止近视生长。

另有研究显示室外活动与近视发展呈反比关系[5],其中一个备受关注的领域是强光对近视发展的保护作用,并可能通过多巴胺信号传导。在户外度过更多时间的儿童会延迟近视发作。[6]这是由于强光,而不是活动或其他因素。[7]这些保护作用归因于视网膜多巴胺水平升高和多巴胺受体激活,因为多巴胺D2样受体拮抗剂螺哌隆酮(Spiperone)能够阻断强光的这种保护作用。[8]多巴胺的释放会根据光照强度和图像对比度进行调控,两者都由感光细胞检测且与近视发展有关。因此,暴露于强光可以是通过增加视网膜中的多巴胺水平来控制近视发展的环境干预式方法。视网膜多巴胺可通过触发视网膜或脉络膜中的一氧化氮释放而诱发脉络膜增厚和眼球生长抑制。[9]

此外,动物研究显示亮光减少近视是在标准实验室条件下完成的,其亮度和色度没有改变,不能模拟真实世界的自然环境,因为自然环境中的亮度会根据一天中的时间不断变化。这可能造成室外光线照射对雏鸡形觉剥夺性近视发育具有不同影响。[10] 这些观察结果表明,治疗近视的药理学方法可能比环境干预更有效。

从开发基于多巴胺的治疗策略的角度来看,多巴胺药物治疗儿童具有一定的潜在毒理学后果。多巴胺是脑和视网膜中的主要神经递质,也是心血管,内分泌和免疫功能的重要生理调节剂。在发育中的大脑中,多巴胺系统的过度激活已被证明会产生类似精神分裂症的行为改变。[11]与这些临床前研究一致,在少数近视儿童的L-DOPA(3,4-二羟苯丙氨酸)临床试验中发现了一些轻微的副作用,包括恶心,头痛,疲劳,情绪变化,呕吐,头晕,口干,食欲下降和梦魇;联合使用卡维多巴(一种外周多巴脱羧酶抑制剂)与L-DOPA可减少副作用。[12]此外,在开发用兴奋剂治疗注意力缺陷障碍(ADD)/注意力缺陷多动障碍(ADHD)的儿童,通过增加大脑中的细胞外多巴胺水平(包括利他林,Adderall和Dexedrine),常见的副作用包括感觉不安和紧张,睡眠困难,食欲不振,头痛,胃部不适,烦躁,情绪波动,抑郁,头晕,心跳加速和抽搐。[13]美国心脏协会建议所有人,包括儿童,在开始兴奋剂之前进行心脏评估。[14]此外,ADD/ADHD药物对年轻、正在发育的大脑的长期影响尚不清楚。

然而,多巴胺受体激动剂可提供具有较少副作用的治疗选择。 多巴胺受体激动剂目前已获FDA批准用于治疗各种疾病,包括帕金森病,不安腿综合征和糖尿病。然而,在发育的关键时期使用多巴胺受体激动剂可能是有害的。 多巴胺受体对葡萄糖代谢具有深远影响。[14]因此,在多巴胺受体激动剂可被视为儿童的抗近视药物之前,必须对多巴胺对视觉和全身功能的可能副作用进行广泛研究。

* 翻译,裁剪自: Zhou X, Pardue MT, Iuvone PM, Qu J. Dopamine signaling and myopia development: What are the key challenges. Prog Retin Eye Res. 2017; 61: 60–71.

Read More

Enable Night Color for KDE on Archlinux

Xorg

(2019.10.25 Update) The Night Color settings are now available on X11 since Plasma 5.16.90.

There is no direct option for Night Color On KDE Plasma 5.16.4. You need to get the dependence redshift installed.

Location Service

Run geoclue

Run redshift for the first time in terminal. If you get the following output:

1
2
3
4
Trying location provider `geoclue2'...
Using provider `geoclue2'.
Using method `randr'.
Waiting for initial location to become available...

Then you need to ensure geoclue is running without error. AFAIK, geoclue.service is depended on avahi-daemon.service and ModemManager.service, you should start the services to make geoclue run:

1
2
3
sudo systemctl start avahi-daemon.service
sudo systemctl start ModemManager.service
sudo systemctl restart geoclue.service

Allow redshift to access geoclue service [1]

Add following text to /etc/geoclue/geoclue.conf:

1
2
3
4
[redshift]
allowed=true
system=false
users=

Then restart geoclue.service.

KDE Addon

To make it easier for settings on redshift, an addon is necessary.

  1. Open KDE Software Center(org.kde.discover). Search “Redshift Control” and install it.
  2. Add the widget “Redshift Control” on desktop or anywhere you want to put it on.
  3. Right-click the widget, configure the redshift.
  4. Left-click, open Night Color.

Wayland

There’s a direct option for Night Color for Wayland.


  1. https://github.com/jonls/redshift/issues/158

解决小米Max账户锁问题

表弟的小米手机被锁上了,型号是小米Max标准版(高通650 hydrogen)。根据他的说法,手机在某次重新启动无缘无故后出现了账户锁,就是如下图所示的样子。

似乎这种情况是随机出现的,为了突击检查你的手机属不属于你。然而锁机的做法并没有什么用处,你不能指望这个锁能让那些捡到手机不还的人放弃这部手机。

这部小米Max在给到他手中他之前,已经用他爸爸的弃用的手机号绑定了小米账号,而他爸早就忘了当时注册的时候填了个什么密码,所以从官方渠道解决锁定问题会相当麻烦。

TL;DR

免责声明: 此操作有风险,任何依照本文章步骤操作导致的问题由用户自身承担。
若您的小米Max(hydrogen)设备遇到账户锁问题,遵循以下步骤:

打开USB调试

  1. 下载所需文件
  1. 同时按住手机音量加、音量减和电源键进入EDL(Emergency Download)模式,并通过USB将手机与电脑连接。注意:此模式下屏幕不会开启,只能通过启动时的振动判断是否进入EDL模式。
  2. 解压MI ACCOUNT REMOVE FILE,将解压后的文件夹载入MiFlash。点击refresh按钮刷新设备,并点击flash刷入。如图:
  3. 断开USB连接,按住电源键强制重新开机。进入系统后将WLAN与移动数据关闭,否则锁定界面会重新出现。
  4. 在开发者选项中打开“USB调试”。

禁止账号验证活动

  1. 在电脑上安装adb,并重新连接手机。
  2. 打开cmd,输入adb shell,此时手机出现“允许USB调试吗?”提示,确认即可。
  3. 在cmd中输入
1
2
3
4
adb shell
pm uninstall -k --user 0 com.xiaomi.finddevice
pm uninstall -k --user 0 com.xiaomi.account
pm uninstall -k --user 0 com.android.provision
  1. 重新连接网络,此时锁定界面应不再出现。

尝试

官方Rec清除数据

算了,这用脑子想想就能知道不可能解决问题。未解开Bootloader刷入第三方Rec之前,不管用什么方法清除数据,都会不可避免地保留最后一次登陆的小米账户数据。

重新刷入官方ROM

目前已知小米Max最早版本为MIUI7.5,下载地址在此处。是的,同样无法解决问题。但是似乎新版本(MIUI10)在使用adb卸载finddevice服务后MIUI系统会不断尝试调用finddevice,这可能会导致一定的性能下降。

我一直以为手机除了正常系统外只有Recovery和Fastboot两种模式,没想到居然还有一种紧急救援模式EDL。[1]EDL模式用于绕过Bootloader,执行刷机救砖操作。EDL只存在于搭载高通处理器的小米设备上,且2017及以后发布的小米设备无法通过EDL模式绕过设备锁。[2]

根据网络上的说法:“开发版和稳定版是两个完全不同的系统,里面的data数据都不同,所以互刷能完全清除小米账号。”[3]于是我尝试着刷入了最新版的开发版,结果并没有什么改变,在开机设置系统时我就被要求激活设备。同样的,刷回旧版本(MIUI7.5)也无济于事,联网后仍然需要激活,否则会被锁住无法进行任何操作。

刷入TWRP

既然能够进入EDL模式,就说明还有不少方法可以尝试。毕竟EDL这东西本身就像是个低配版的Fastboot,能通过它对手机的文件系统进行操作。

在MIUI7.5系统下,进入EDL后,用MiFlash刷入第三方Rec——这应该是一条可行的道路,但是当时我一不小心没注意到clean all选项被勾选上了,于是我成功地把MIUI7.5擦除了。至于是否能够刷上,你可以自己尝试。

可使用MiFlash刷入的TWRP包在此处,由xda论坛提供。

刷入镜像暂时移除锁定界面

网络上有篇博文写道使用他提供的REMOVE FILE能够删除小米账号。[4]那我们就来动手试试吧。

  1. Fastboot不支持被锁设备的刷机操作,所以需要进入EDL救援模式。进入EDL有多种方式,譬如使用EDL线、拆开手机后盖短接测试点等。不过小米Max似乎可以直接通过同时按住音量加、音量减和电源键进入EDL模式。
  2. 将手机通过USB与电脑连接。此时在_设备管理器_中端口下应出现Qualcomm HS-USB QDLoader 9008一栏。
  3. 下载上述REMOVE FILE并解压,下载能与EDL交互的MiFlash
  4. 打开MiFlash,载入解压后的文件夹并刷入该删除文件
    MiFlash
  5. 结束后断开USB连接,长按电源键重启设备。

此时进入系统后在短时间内不会锁定,请迅速关闭网络以防“查找设备”服务上线,否则设备将重新上锁。

使用adb卸载小米服务[5]

安装adb

你可以参照这篇教程,并请善用搜索引擎。

打开USB调试

在暂时解锁的小米手机的连续点按MIUI版本号,直到开发者模式开启,然后在开发者选项中允许USB调试。将手机与电脑通过USB连接,打开电脑终端,输入adb shell获取权限,在手机上允许来自此电脑的调试即可。

卸载小米服务

在终端中再次输入adb shell,此时应进入adb终端。在adb终端中输入:

1
2
3
pm uninstall -k --user 0 com.xiaomi.finddevice
pm uninstall -k --user 0 com.xiaomi.account
pm uninstall -k --user 0 com.android.provision

最后重新连接网络。

结语

使用本方法解决锁定问题并不完美,已知缺点如下:

  1. 无法使用小米提供的云服务
  2. 系统性能下降
  3. 可能导致bootloop
  4. 新版本MIUI会不断出现“Finddevice service destroyed”,但不影响使用

本文介绍的解决方案是极其简陋的,你可以自行尝试其他方法,如使用第三方Rec完全清除数据。


  1. http://en.miui.com/thread-307139-1-1.html

  2. https://www.reddit.com/r/Xiaomi/wiki/edl

  3. http://www.miui.com/forum.php?mod=viewthread&tid=9610315

  4. https://alltechminer.blogspot.com/2019/01/remove-mi-account-on-mi-max-or-2016002.html

  5. https://www.youtube.com/watch?v=JoYdVrYM5Lo

Semigroup is a Superclass of Monoid

Semigroup is now a superclass of Monoid since base-4.11.0.0(released in 2018.04).

So if you try to implement the exercise Optional Monoid in Haskell Programming from First Principle:

1
2
3
4
5
6
7
8
9
10
11
12
module OptionalMonoid where

data Optional a =
Nada
| Only a
deriving (Eq, Show)

instance Monoid a => Monoid (Optional a) where
mempty = Nada
mappend Nada (Only x) = Only $ mappend mempty x
mappend (Only x) Nada = Only $ mappend x mempty
mappend (Only x) (Only y) = Only $ mappend x y

will give an error:

1
2
3
4
5
6
7
8
9
10
haskell/cp15.hs:8:10: error:
• Could not deduce (Semigroup (Optional a))
arising from the superclasses of an instance declaration
from the context: Monoid a
bound by the instance declaration at haskell/cp15.hs:8:10-40
• In the instance declaration for ‘Monoid (Optional a)’
|
8 | instance Monoid a => Monoid (Optional a) where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.

The solution is to write a Semigroup instance of (<>) for mappend, and then define mempty in Monoid instance.

You can just ignore mappend, since it’s only around for compatibility reasons now.

1
2
3
4
5
6
7
8
9
10
data Optional a = Nada | Only a deriving (Eq, Show)

instance Monoid a => Monoid (Optional a) where
mempty = Nada

instance Semigroup a => Semigroup (Optional a) where
Nada <> (Only x) = Only x
(Only x) <> Nada = Only x
Nada <> Nada = Nada
(Only x) <> (Only y) = Only $ x <> y

Reference

Could not deduce (Semigroup (Optional a)) arising from the superclasses of an instance declaration.https://stackoverflow.com/questions/52237895/could-not-deduce-semigroup-optional-a-arising-from-the-superclasses-of-an-in

Haskell Indentation

Indentations are used to make codes shorter and beginners confused.

The indentations in Haskell has the same effect as the {} parantheses in C++. Compare

1
2
3
4
5
-- Haskell:
if a == b then
putStrLn "a equals to b"
else
putStrLn "a doesn't equal to b"

with:

1
2
3
4
5
6
7
// C++:
if (a==b) {
std::cout << "a equals to b";
}
else {
std::cout << "a doesn't equal to b";
}

Yep, likely the same.

Read More