# Characters

In Guile, a character is written #\name, where name is the name of the character. For example the character a is written #\a and space is written #\space.

In this chapter we will create a small program that determines whether the input letter belongs to an alphabetical range. For example: does the letter P belong to the interval [E; Z]? The answer is yes. Does the letter A belong to the interval [O; T] ? The answer is no.

## Write the test first

;; characters-test.scm

(use-modules (srfi srfi-64)
(characters))

(test-begin "harness-characters")

(test-assert "a char belongs to its own interval"
(char-belongs #\a (cons #\a #\a))

(test-end "harness-characters")


$guile --no-auto-compile -L . characters-test.scm ## Write the minimal amount of code for the test to run and check the failing test output The terminal should return a backtrace and the following message: no code for module (characters). Now I think you get it, you have to create the module. ;; characters.scm (define-module (characters))  Restart the test. You should get the following feedback: $ guile --no-auto-compile -L . characters-test.scm
%%%% Starting test harness-characters (Writing full log to "harness-characters.log")
characters-test.scm:6: FAIL a char belongs to its own interal
# of unexpected failures 1


In the managed log, you will see the indication (unbound-variable #f "Unbound variable: ~S" (char-belongs) #f). So the next thing to do is to define char-belongs.

;; characters.scm

(define-module (characters))

(define-public (char-belongs char interval)
"Return true if char belongs to interval, else return false."
#f)


Restart the tests.

$guile --no-auto-compile -L . characters-test.scm %%%% Starting test harness-characters (Writing full log to "harness-characters.log") characters-test.scm:6: FAIL a char belongs to its own interal # of unexpected failures 1  The code compiles. The test fails because the char-belongs procedure does not return #t (it returns #f). $ cat harness-characters.log
%%%% Starting test harness-characters
Group begin: harness-characters
Test begin:
test-name: "a char belongs to its own interal"
source-file: "characters-test.scm".
source-line: 6
source-form: (test-assert "a char belongs to its own interal" (char-belongs #\a (list #\a #\a)))
Test end:
result-kind: fail
present value: #f
Group end: harness-characters
# of unexpected failures 1


## Write enough code to make it pass

Here you have one thing to do to pass the test: modify char-belongs to return #t.

;; characters.scm

(define-module (characters))

(define-public (char-belongs char interval)
"Return true if char belongs to interval, else return false."
#t)


Restart the tests :

 guile --no-auto-complete -L . characters-test.scm
%%%% Starting test harness-characters (Writing full log to "harness-characters.log")
# of expected passes 1


The test passes! The modification was enough.

## Refactor

Here, I will extract a variable in the tests to carry the intention that the character #\a is a letter of the alphabet chosen arbitrarily.

;; characters-test.scm

(use-modules (srfi srfi-64)
(characters))

(test-begin "harness-characters")

(define DUMMY_LETTER #\a)

(test-assert "a char belongs to its own interval"
(char-belongs DUMMY_LETTER (cons DUMMY_LETTER DUMMY_LETTER)))

(test-end "harness-characters")


Of course, you hurry to launch the tests to validate the refactoring.

$guile --no-auto-compile -L . characters-test.scm %%%% Starting test harness-characters (Writing full log to "harness-characters.log") # of expected passes 1  Next! ## Write the test first ;; characters-test.scm (use-modules (srfi srfi-64) (characters)) (test-begin "harness-characters") (define DUMMY_LETTER_1 #\a) (define DUMMY_LETTER_2 #\b) (test-assert "a char belongs to its own interval" (char-belongs DUMMY_LETTER_1 (cons DUMMY_LETTER_1 DUMMY_LETTER_1)))) (test-assert "a char does not belong to another char interval" (not (char-belongs DUMMY_LETTER_2 (cons DUMMY_LETTER_1 DUMMY_LETTER_1)))) (test-end "harness-characters")  ## Try and run the test $ guile --no-auto-compile -L . characters-test.scm

Yippee, it's a failure!

$guile --no-auto-compile -L . characters-test.scm %%%% Starting test harness-characters (Writing full log to "harness-characters.log") characters-test.scm:12: FAIL a char does not belong to another char interval # of expected passes 1 # of unexpected failures 1  ## Write the minimal amount of code for the test to run and check the failing test output No compilation error. The reason for the failure is that the char-belongs procedure constantly returns #t. However, in this second test, we want it to return #f. is begin: test-name: "a char does not belong to another char interval" source-file: "characters-test.scm". source-line: 12 source-form: (test-assert "a char does not belong to another char interval" (not (char-belongs DUMMY_LETTER_2 (cons DUMMY_LETTER_1 DUMMY_LETTER_1)))) Test end: result-kind: fail present value: #f  ## Write enough code to make it pass Here's what I propose to you: ;; characters.scm (define-module (characters)) (define-public (char-belongs char interval) "Return true if char belongs to interval, else return false." (if (char=? char #\a) #t #f))  If you run the tests again, you'll see that this change does the trick! $ guile --no-auto-compile -L . characters-test.scm
%%%% Starting test harness-characters (Writing full log to "harness-characters.log")
# of expected passes 2


The tests written so far do not require the use of the second parameter. So I don't bother.

## Refactor

In the test harness (in other words, the test suite), I extract a variable that contains the range from the letter a.

;; characters-test.scm

(use-modules (srfi srfi-64)
(characters))

(test-begin "harness-characters")

(define DUMMY_LETTER_1 #\a)
(define DUMMY_LETTER_2 #\b)

(define INTERVAL_DUMMY_LETTER_1 (cons DUMMY_LETTER_1 DUMMY_LETTER_1))

(test-assert "a char belongs to its own interval"
(char-belongs DUMMY_LETTER_1 INTERVAL_DUMMY_LETTER_1))

(test-assert "a char does not belong to another char interval"
(not (char-belongs DUMMY_LETTER_2 INTERVAL_DUMMY_LETTER_1))))

(test-end "harness-characters")


Run the tests to confirm that everything is in order. Then, in the tested code, I remove the if that is too much.

;; characters.scm

(define-module (characters))

(define-public (char-belongs char interval)
"Return true if char belongs to interval, else return false."
(char=? char #\a))


No regression, everything is in order!

$guile --no-auto-compile -L . characters-test.scm %%%% Starting test harness-characters (Writing full log to "harness-characters.log") characters-test.scm:17: FAIL a letter preceding the lower bound of the interval does not belong to it # of expected passes 2 # of unexpected failures 1  It is indeed our new test that fails. ## Write the minimal amount of code for the test to run and check the failing test output No compilation error, the test fails because we wait for the value #f but the procedure returns #t. Test begin: test-name: "a letter preceding the lower bound of the interval does not belong to it" source-file: "characters-test.scm". source-line: 17 source-form: (test-assert "a letter preceding the lower bound of the interval does not belong to it" (not (char-belongs DUMMY_LETTER_1 (cons #\b #\c)))) Test end: result-kind: fail present value: #f  ## Write enough code to make it pass ;; characters.scm (define-module (characters)) (define-public (char-belongs char interval) "Return true if char belongs to interval, else return false." (and (char<=? char (car interval)) (char>=? char (cdr interval))))  Everything passes! $ guile --no-auto-compile -L . characters-test.scm
%%%% Starting test harness-characters (Writing full log to "harness-characters.log")
# of expected passes 3


## Refactor

I extract the new variable in the tests and I take care to use them properly.

I take care to use a, so-called, business language. I will therefore change the name of my procedure from char-belongs to letter-belongs.

I also pay attention to the language conventions: a ? at the end of a predicate. Our letter-belongs procedure is one of them.

;; characters-test.scm

(use-modules (srfi srfi-64)
(characters))

(test-begin "harness-characters")

(define DUMMY_LETTER_1 #\a)
(define DUMMY_LETTER_2 #\b)
(define DUMMY_LETTER_3 #\c)

(define INTERVAL_DUMMY_LETTER_1 (cons DUMMY_LETTER_1 DUMMY_LETTER_1))

(test-assert "a letter belongs to its own interval"
(letter-belongs? DUMMY_LETTER_1 INTERVAL_DUMMY_LETTER_1))

(test-assert "a letter does not belong to another letter's interval"
(not (letter-belongs? DUMMY_LETTER_2 INTERVAL_DUMMY_LETTER_1)))))

(test-equal "a letter preceding the lower bound of the interval does not belong to it"
#f
(letter-belongs? DUMMY_LETTER_1 (cons DUMMY_LETTER_2 DUMMY_LETTER_3)))

(test-end "harness-characters")


I extract procedures to make the intention more explicit and to respect the principle of Single Layer Abstraction (SLA).

Same for business names and language conventions.

;; characters.scm

(define-module (characters))

(define-public (letter-belongs? letter interval)
"Return true if letter belongs to interval, else return false."

(define (preceed-lower-bound? letter)
(char<=? letter (car interval)))

(define (follow-upper-bound? letter)
(char>=? letter (cdr interval)))

(and (preceed-lower-bound? letter) (follow-upper-bound? letter))


You will notice that the preceed-lower-bound? and follow-upper-bound? procedures use the interval variable without needing to have it as a parameter. This is because they are defined locally in the letter-belongs? procedure. It is said that they capture the environment where the interval variable is defined and can then refer to it. This is called a closure. It is a specificity of the functional paradigm!

## Conclusion

• More TDD practice
• Characters, comparison
• Functional Paradigm: the closure.