From e11e132a59d29f2f0568b29117b05cee2a5f45a7 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Sat, 24 Oct 2020 18:59:50 +0100 Subject: [PATCH 1/5] Add a state monad as the foundation of Cubes * Each cube will be a state monad and the description of how to change the state. --- core/fg42/cube.el | 45 +++++++++++++++++++++++++ core/fg42/state.el | 84 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 core/fg42/cube.el create mode 100644 core/fg42/state.el diff --git a/core/fg42/cube.el b/core/fg42/cube.el new file mode 100644 index 0000000..35cd2c1 --- /dev/null +++ b/core/fg42/cube.el @@ -0,0 +1,45 @@ +;;; Cube --- Cube library of FG42 -*- lexical-binding: t; -*- +;; +;; Copyright (c) 2010-2020 Sameer Rahmani & Contributors +;; +;; Author: Sameer Rahmani +;; URL: https://gitlab.com/FG42/FG42 +;; Version: 3.0.0 +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;;; Commentary: +;; Cubes are the building blocks of any `FG42' editor. Each `cube' is a +;; unit which defines different abilities in a deterministic and idempotent +;; way. Cubes are composable and a composition of cubes creates an editor. +;; +;;; Code: +(require 'fg42/utils) +(require 'fg42/state) + + +(defun fg42/cube-apply (state cube) + "Apply the given CUBE to the given STATE. +It returns a cons of (new-state . cube)." + (cons state cube)) + + +(defun fg42/cube-bind (m f) + "Bind the state monad M with the given function F." + (let ((binder (fg42/state-bind-maker #'fg42/cube-apply))) + (funcall binder m f))) + + +(provide 'fg42/cube) +;;; cube.el ends here diff --git a/core/fg42/state.el b/core/fg42/state.el new file mode 100644 index 0000000..eb06b0b --- /dev/null +++ b/core/fg42/state.el @@ -0,0 +1,84 @@ +;; State --- State library of FG42 -*- lexical-binding: t; -*- +;; +;; Copyright (c) 2010-2020 Sameer Rahmani & Contributors +;; +;; Author: Sameer Rahmani +;; URL: https://gitlab.com/FG42/FG42 +;; Version: 3.0.0 +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;;; Commentary: +;; Cubes are the building blocks of any `FG42' editor. Each `cube' is a +;; unit which defines different abilities in a deterministic and idempotent +;; way. Cubes are composable and a composition of cubes creates an editor. +;; +;;; Code: +(require 'fg42/utils) + + +(defun fg42/state-return (x) + "Return a state monad containing X." + (lambda (state) + (cons state x))) + + +(defun fg42/state-bind-maker (binder-fn) + "Return a bind function for state monad. +It creates a bind function that binds monadic functions by applying BINDER-FN +to the return state and the value inside the monad before binding them. + +The BINDER-FN has to return a cons in form of (state . prev-v)." + (lambda (m f) + "Applys F on the M monad." + (lambda (state) + (let* ((v (funcall m state)) + (new-v (funcall (f (cdr v)) (car v)))) + (funcall binder-fn + ;; State + (car new-v) + ;; Value of in the monad from M + (cdr new-v)))))) + + +(defun fg42/state-unit () + "Create a new state monad. +This monad describes the entire editor configuration" + ;; Why not a cl-struct ? + ;; Because first, we want it to be human readable + (lambda (state) + (cons + (list '(:mode->keybindings . ()) + '(:mode->prefix . ()) + '(:mode->fn . ()) + '(:cubes . ())) + ()))) + + +(defun fg42/state-get (k &optional default) + "Return the value of the given K from the STATE. +It will return DEFAULT in case of a missing key." + (lambda (state) + (or (cdr (assoc k state)) default))) + + + +(defun fg42/state-cons (k v) + "Add the given value V to the list addressed by key K on STATE." + (lambda (state) + (cons (cons k (cons v (or (fg42/state-get state k) '()))) state))) + + +(provide 'fg42/state) +;;; state.el ends here From 149d9c6b008a19b574fbf5a23030273c2bfa90c0 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Sat, 24 Oct 2020 19:05:39 +0100 Subject: [PATCH 2/5] Add threading macros to the utils module --- core/fg42/utils.el | 29 +++++++++++++++++++++++++++++ lib/fg42/wm.el | 1 + 2 files changed, 30 insertions(+) diff --git a/core/fg42/utils.el b/core/fg42/utils.el index 402fb59..ff180ea 100644 --- a/core/fg42/utils.el +++ b/core/fg42/utils.el @@ -129,5 +129,34 @@ or (last paths)))) +(defmacro -> (x &optional form &rest more) + "Thread the expr through the forms FORM and rest of form in MORE. +Insert X as the second item in the first form, making a list of +it if it is not a list already. If there are more forms, insert +the first form as the second item in second form, etc." + (declare (debug (form &rest [&or symbolp (sexp &rest form)]))) + (cond + ((null form) x) + ((null more) (if (listp form) + `(,(car form) ,x ,@(cdr form)) + (list form x))) + (:else `(-> (-> ,x ,form) ,@more)))) + + +(defmacro ->> (x &optional form &rest more) + "Thread the expr through the forms FORM and the rest at MORE. +Insert X as the last item in the first form, making a list of +it if it is not a list already. If there are more forms, insert +the first form as the +last item in second form, etc." + (declare (debug ->)) + (cond + ((null form) x) + ((null more) (if (listp form) + `(,@form ,x) + (list form x))) + (:else `(->> (->> ,x ,form) ,@more)))) + + (provide 'fg42/utils) ;;; utils.el ends here diff --git a/lib/fg42/wm.el b/lib/fg42/wm.el index 3f3c732..4868d06 100644 --- a/lib/fg42/wm.el +++ b/lib/fg42/wm.el @@ -84,6 +84,7 @@ ;; Bind "s-r" to exit char-mode and fullscreen mode. ([?\s-r] . exwm-reset) ([?\s-g] . keyboard-quit) + ([8388640] . other-window) ;; Bind "s-w" to switch workspace interactively. ([?\s-w] . exwm-workspace-switch) ;; Bind "s-0" to "s-9" to switch to a workspace by its index. From 4531c8cd81dcc0a3af0678142d8e33de2cc595fd Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Sat, 24 Oct 2020 21:44:12 +0100 Subject: [PATCH 3/5] Add and functions for the cube module --- core/fg42/cube.el | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/core/fg42/cube.el b/core/fg42/cube.el index 35cd2c1..b32a1f1 100644 --- a/core/fg42/cube.el +++ b/core/fg42/cube.el @@ -25,20 +25,53 @@ ;; way. Cubes are composable and a composition of cubes creates an editor. ;; ;;; Code: + +(require 'seq) (require 'fg42/utils) (require 'fg42/state) (defun fg42/cube-apply (state cube) "Apply the given CUBE to the given STATE. -It returns a cons of (new-state . cube)." - (cons state cube)) +It returns a new state." + state) -(defun fg42/cube-bind (m f) - "Bind the state monad M with the given function F." - (let ((binder (fg42/state-bind-maker #'fg42/cube-apply))) - (funcall binder m f))) +(defun fg42/cube-bind (m1 m2) + "Bind the M1 to M2. +M1 and M2 are state monads. See `fg42/utils'" + (lambda (state) + (let* ((v (funcall cube1 state))) + (funcall cube2 + (fg42/cube-apply + ;; State + (car v) + ;; Value of in the monad from M + (cdr v)))))) + + +(defun fg42/cube-compose (cube1 cube2) + "Compose CUBE1 and CUBE2 to create a new cube. +For example `(fg42/cube-compose #\'some-cube #\'some-other-cube)'" + (lambda () + (fg42/cube-bind + (funcall cube1) + (funcall cube2)))) + + +(defun fg42/cube-identity () + "Cube identity function." + (lambda (state) + (cons state '()))) + + +(defun fg42/cubes (&rest cubes) + "Create a new cube out of the given list of CUBES." + (seq-reduce (lambda (cube1 cube2) + (fg42/cube-compose cube1 cube2)) + cubes + #'fg42/cube-identity)) + (provide 'fg42/cube) From 531e4603e8bee9ae763badb024df0447db3332c0 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Mon, 26 Oct 2020 19:28:34 +0000 Subject: [PATCH 4/5] Update 'cubes' function to accept monadic value instead of cubes --- core/fg42/cube.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/fg42/cube.el b/core/fg42/cube.el index b32a1f1..d902892 100644 --- a/core/fg42/cube.el +++ b/core/fg42/cube.el @@ -68,10 +68,9 @@ For example `(fg42/cube-compose #\'some-cube #\'some-other-cube)'" (defun fg42/cubes (&rest cubes) "Create a new cube out of the given list of CUBES." (seq-reduce (lambda (cube1 cube2) - (fg42/cube-compose cube1 cube2)) + (fg42/cube-bind cube1 cube2)) cubes - #'fg42/cube-identity)) - + (fg42/cube-identity))) (provide 'fg42/cube) From c5bdda95ce704c3c5454220c18e6abf28d4dd105 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Wed, 11 Nov 2020 12:39:42 +0000 Subject: [PATCH 5/5] Fix the merge problem in fpkg --- lib/fpkg.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/fpkg.el b/lib/fpkg.el index bee82ad..8386f70 100644 --- a/lib/fpkg.el +++ b/lib/fpkg.el @@ -125,10 +125,10 @@ `(fg42-install-extension ,(eval pkgname)) `(use-package ,(eval pkgname) ,@details))) -;; (defun depends-on (pkgname &rest args) -;; "Install the package PKGNAME with respect to the ARGS." -;; (let ((pkg (apply 'make-fpkg-dependency :name pkgname args))) -;; (puthash pkgname pkg required-packages))) +(defun depends-on (pkgname &rest args) + "Install the package PKGNAME with respect to the ARGS." + (let ((pkg (apply 'make-fpkg-dependency :name pkgname args))) + (puthash pkgname pkg required-packages))) (provide 'fpkg)