I was in a world where a code describing type signature was longer than a code telling what a program does. I don't want to bring that situation back.
I expect AI code completion to replace static typing. Ruby Type Signature (RBS) and similar things must be inferred automatically from a database or other data sources.
; caught WARNING:
; Derived type of STATIC1::A is
; (VALUES FIXNUM &OPTIONAL),
; conflicting with its asserted type
; STRING.
; See also:
; The SBCL Manual, Node "Handling of Types"
So SBCL - a Common Lisp implementation, can check type in compile-time. Anyway, a programmer needs to read warnings.
Last week, I watched a video about coding and Docker configuration on a TV. I couldn't read any line of code. Then I thought about visually impaired people. How do they code? Every student in Thailand must learn to code. I presume the situation is similar in every country.
People widely use text-to-speech services these days. I cannot find any text-to-speech service for reading source code aloud. Let's assume we have a modified version of a text-to-speech service.
So I looked at source codes in different programming languages on the CodeRosetta website. I perceive Python code blocks by their visual structure purely. To read Python source code, I have to encode its visual structure, namely indents to sounds. Reading a nested code block won't be easy to understand. For example, reading twelve leading white spaces aloud will be very strange. In Lisp, reading open parenthesis and close parenthesis is more straightforward, but I will forget which parenthesis. So the best form of code blocks is in QuickBasic, which has different keywords between different kinds of blocks. For example, FOR with NEXT, and IF with END IF. Later I got a comment from Lemmy.ml, which told me that Ada also has different keywords between different kinds of blocks. Another idea from Lemmy.ml is the reader must convert the Python code block into a similar form as Ada or QuickBasic before reading.
MBasic refers to code by line numbers instead of code blocks. However, by listening to five lines, I forgot the line number. For example, when I heard gosub 70, I forgot what was at line 70.
In X86 Assembly, a programmer labels only the line that the program will jump to it. So X86 Assembly code looks much better than MBasic.
Still, coding in X86 Assembly can be exhaustive in many cases. For example, X86 Assembly doesn’t support recursion. Writing quick sort in X86 Assembly can be too difficult for learning to code.
Haskell doesn’t rely on code blocks. However, reading the symbols, for example, >>= is challenging. Prolog’s symbols are easy to read. For example, we can read :- as IF. Anyway, the Prolog programming paradigm is different from the mainstream one now. So Erlang, whose syntax is similar to Prolog, is a more practical alternative.
In brief, Erlang is a practical, less visual-centric program language. Because it mostly relies on names instead of code blocks, reading names aloud is much easier than reading code blocks aloud. Furthermore, the Erlang programming paradigm is more mainstream now.
#################### F1 ###################
Evaluation count : 6 in 6 samples of 1 calls.
Execution time sample mean : 3.811858 sec
Execution time mean : 3.812064 sec
#################### F2 ###################
Evaluation count : 6 in 6 samples of 1 calls.
Execution time sample mean : 1.490624 sec
Execution time mean : 1.490777 sec
F1, which is the lazy sequence version, took 3.812064 seconds. F2, which is the transducer version, took 1.490777. So the transducer version is 155.71% faster than the lazy sequence version.
In brief, this biased experiment shows the transducer version is much faster than the pure lazy sequence version.
I recommend Clojure as the first programming language to learn for beginners. I recommend "Poetry of Programming" as the first book for learning Clojure. I recommend "Clojure for the Brave and True" as the second book for learning Clojure.
Lisp taught me to create functions that transform data step by step instead of adding mutable variables into loops. An immutable data structure is important for transforming data step by step without messing up existing data. Lisp's default data structure is a singly linked list.
Lisp macro is easy to write compared to competitors. Rust's macro is powerful. Rust's macro is about transforming a TokenStream, and #Lisp's macro is about transforming a list. However, I prefer transforming a list to a TokenStream since I don't have to learn new structures and new commands for manipulating a new structure.
And it has too many "return" statements. Anyway, I suppose Python doesn’t prefer a recursion. So this isn’t the case when we code in the Python way.
The code above concatenate Python's lists instead of creating a linked list like in Lisp.
So I made this experiment.
importtimen=100# Concatinating vectors
v=[]t1=time.time_ns()foriinrange(n):v=[i]+vt2=time.time_ns()# Creating cons cells for a singly link list
l=Nonet3=time.time_ns()foriinrange(n):l=(i,l)# Creating a cons cell
t4=time.time_ns()print((t2-t1)/(t4-t3))
At n = 100, concatenating vectors require 2 times of running time compared to creating cons cells for a linked list. So I keep using linked lists.
I want to install a specific version of RocksDB across different machines with different operating systems. Thus I can't rely on APT or other package management systems. I followed INSTALL.md by running make on the bundled Makefile, but last Saturday, I found that RocksDB couldn't read Snappy compressed data, although I installed the "libsnappy-dev" package. I tried many different ways to enable Snappy support. Then I decided to use CMake, which appeared only once in INSTALL.md. Now it works. My install script looks like this:
แต่ถ้า Fields มีเยอะ ๆ สัก 10 Fields ขึ้นไป Code ก็จะเริ่มดูยากยาวมาก การใช้ Pydantic model เข้ามาช่วยก็จะจัดการง่ายขึ้น ปกติเราใช้ Pydantic model ใน Body แบบ JSON กันอยู่แล้วแต่สำหรับ Form Data จะมีลูกเล่นนิดหนึ่ง ค้นหาใน internet ไปเจอวิธีที่คิดว่าง่ายที่สุดละเลยบันทึกไว้สักหน่อย
// define variablesletcamera_id='';// current camera idletstream=null;// current streamletdevices=[];// all cameraconstdiv=document.createElement('div');// parent div playgroundconstcamera_list=document.createElement('select');// camera dropdownconstcamera_label=document.createElement('h3');// current selected cameraconstvideo=document.createElement('video');// video object// scan all camerasasyncfunctionscan_cameras(){letcamera_found=false;awaitnavigator.mediaDevices.enumerateDevices().then((deviceInfos)=>{deviceInfos.forEach((deviceInfo)=>{if(deviceInfo.kind==='videoinput'){if(!camera_found){camera_label.textContent=`Camera: ${deviceInfo.label}`;camera_id=deviceInfo.deviceIdcamera_found=true}devices.push(deviceInfo)}})})div.appendChild(camera_label);}// change camera by idasyncfunctionchange_camera(e){camera_id=e.target.value;camera_label.textContent=`Camera: ${devices.filter(i=>i.deviceId==camera_id)[0].label}`;awaitstart_camera();}// stop all active cameraasyncfunctionstop_camera(){if(stream){awaitstream.getVideoTracks().forEach(track=>track.stop());}}// start camera by selected idasyncfunctionstart_camera(){awaitstop_camera();stream=awaitnavigator.mediaDevices.getUserMedia({video:{deviceId:{exact:camera_id},audio:false}});video.srcObject=stream;awaitvideo.play();}// create camera list dropdownfunctioncreate_camera_select(){camera_list.appendChild(newOption("Select Camera",0))devices.forEach((device,i)=>{camera_list.appendChild(newOption(device.label||`Camera ${i+1}`,device.deviceId));})div.appendChild(camera_list);camera_list.addEventListener("change",change_camera);}// take photoasyncfunctiontakePhoto(quality){awaitscan_cameras();create_camera_select();constcapture=document.createElement('button');capture.textContent='Capture';div.appendChild(capture);video.style.display='block';document.body.appendChild(div);div.appendChild(video);awaitstart_camera();google.colab.output.setIframeHeight(document.documentElement.scrollHeight,true);// Wait for Capture to be clicked.awaitnewPromise((resolve)=>capture.onclick=resolve);constcanvas=document.createElement('canvas');canvas.width=video.videoWidth;canvas.height=video.videoHeight;canvas.getContext('2d').drawImage(video,0,0);awaitstop_camera();div.remove();returncanvas.toDataURL('image/jpeg',quality);}
โปรแกรมตัดเกรดเป็นตัวอย่างของ business logic ที่ดีเลยครับ เพราะว่าหลายครั้งก็มีการเปลี่ยนแปลงกฎเกณฑ์ วิธีคิดเกรดของแต่ละสถาบันไม่เหมือนกันก็ได้ บางวิชาในสถาบันเดียวกันก็ตัดเกรดไม่เหมือนกัน บางมีชามี A B C D F บางวิชามีเกรดแค่ S กับ U
I agree that using JavaScript increases the chance of participation. I released a few versions of Thai word breakers in different programming languages. One on node.js is the most popular. 8 people contributed to the JS-based project compared to 2-3 people in other programming languages. However, JS has a downside too. In 2017, @iporsut and I made an experiment to compare Thai word breakers that we created. JS version running time is 15X of the Rust version. Even by comparing with another dynamic language, the Julia version is faster than the one in JS.
I created a website using node.js in 2014, and it is still running. The performance is good. However, I have a few regrets.
We had a very hard time by install this project on other team members who use Windows 10 because we didn't know how to build a Bcrypt library.
Recently, I have to fix the project without adding any new feature because Express.js was changed, MongoDB was changed, and some packages that I used were abandoned.
It was a small project so I wanted to keep the session storage in RAM, but I can't since I ran 4 node.js processes. Now the project requires Redis as session storage, which causes more troubles for team members, who don't familiar with GNU/Linux, Docker, or WSL.
I knew I’m in a dream. It was a bad dream. I wanted to get out I knew I’m in a dream. I decided to commit a suicide so it would wake me up. I jumped out of a window, closed my eyes. It was pitch black. Eyes opened, I woke up .. .. in … Continue reading inception ?→
A few days ago, I couldn't get a WebSocket library working with another library on Python 3.10. So to avoid those dependencies, I implemented my WebSocket client on a low-level socket API. I implemented one in Common Lisp first. Then I translated it to Python. My WebSocket client is very far away from being completed, but at least it can run.
The first part is import. I imported only three things that are socket, ssl, and uuid.
importsocketimportsslimportuuid
The second part is based on HTTP headers for telling the server to switch to the WebSocket mode.
The third part is reading a content length. I assume that the server keep sending text contents. In read_payload_len(), s.recv(1) read a byte from the socket s. & 0x7F is for masking only 7 bits. If length is 127, the length is in the next 4 bytes instead. If length is 126, the length is in the next 2 bytes. Otherwise the function just returns the length.
In the fourth part, we read a web socket frame. The program determine a frame type from opcode, which in the last 4 bits of the header. I should implement PING-PONG part but I didn't. According to RFC6455, which I forgot to mention before, opcode == 0x1 means the frame is a text frame. So the program reads payload length and reads the payload.
The last part is for opening connections and SSL/TLS wrapper. The function created a socket and wrapped it with TLS/SSL wrapper. The sending upgrade message to ask the server to switch to Websocket mode and the keep reading frames.
Actix has a supervisor that helps to restart an actor under its supervision. However, sometimes it doesn't. 😹
AFAIK something has to send a Die (message) to an actor when something went wrong. The actor has to implement a Die message handler. In the handler, it calls ctx.stop (context stop). When "Actor::stopping(...) == Running::Stop", its poll function will return Poll::Ready. Then its supervisor can perform restarting.
In my case, all of these didn't work because of two reasons.
Actor didn't stop properly. It stopped without calling the stopped function. So I suppose it has no chance to handle the Die message.
Actor kept using the CPU when it went wrong. So the poll function was blocked. Then the actor cannot handle the Die message.
I fixed the first case by calling tokio::time::sleep, and the second case by breaking for the loop.
PS I'm not sure about what I wrote. Please tell me if I missed or misunderstood some things or even everything.
New Rustacians usually ask why Rust has many types of strings. I don't know the original intention. Anyways, it has some advantages as follow:
It minimizes the language core. Rust core has only str. String, OsString, CString are in the standard library, which is not a part of the language core.
Programmer can control performance. For example, &str refers to a byte slice so it should be fast, while String is a wrapper of Vec. String should have more performance penalty from heap memory allocation.
OsString and CString improve interoperability with other programming languages and operating systems. Rust can manipulate C-style strings directly without converting them to native Rust strings. OsString is also similar.
String conversation has a performance penalty. Also sometimes string conversation is not obvious. Some systems don't even use Unicode or other standards.
The alternative of having too many types is treating everything as a byte slice. However, type checking doesn't work well with too few types.
I enjoy using key-value data in dynamic languages. For example, in Python, I can create key-value data for storing the metadata of a document as shown below. I don't discuss why I don't use struct, class, named tuple in this post.
Besides construction, printing a hash table is not so convenient. Maybe one can create a function or macro to make creating/printing a hash table convenient. I still felt that I abused Common Lisp.
My code is usually too buggy when I keep mutating the same variable. So I prefer using an immutable data structure to prevent me from messing things up. Moreover, my key-value data usually do not have more than five keys. So I don't strictly need to use an efficient data structure, namely, hash table or binary search tree. So I use alist (assosiation list). I can construct a list like below:
IMO, it looks concise and convenient. We can retrieve key-value pair with a specific key using the assoc function, which I suppose it does linear search. Linear search can be slow. However, my alist doesn't have a lot of keys.
Instead of replacing a value with another value, I can add a new key-value pair with an existing key, for example: