diff --git a/tp9/aplst.py b/tp9/aplst.py
new file mode 100644
index 0000000000000000000000000000000000000000..3fcca937a0a806af3ea8c780d9dcb84ce5017e1d
--- /dev/null
+++ b/tp9/aplst.py
@@ -0,0 +1,141 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+"""
+:mod:`list` module
+
+:author: FIL - Faculté des Sciences et Technologies - 
+         Univ. Lille <http://portail.fil.univ-lille1.fr>_
+
+:date: 2022, march
+:last revision: 2024, march
+
+Provides 
+
+- a class :class:`ApLst` for non mutable lists
+- an exception :class:`ApLstError` 
+
+
+ApLsts are either empty, either essentially objects with two composants :
+
+#. a *head* composant which represent the head value  of the list,
+#. and a *tail* composant which is represent the tail of the list
+
+"""
+
+
+class ApLstError(Exception):
+    """
+    Exception used by methods
+
+    * ``__init__``
+    * ``head``
+    * ``tail``
+    
+    of class :class:`ApLst`.
+    """
+    def __init__(self, msg):
+        self.message = msg
+
+
+class ApLst():
+    """
+    $$$ list = ApLst()
+    $$$ list.is_empty()
+    True
+    $$e list.head()
+    ApLstError
+    $$$ list2 = ApLst(1, list)
+    $$$ list2.is_empty()
+    False
+    $$$ list2.head()
+    1
+    $$$ list2.tail().is_empty()
+    True
+    $$$ l = ApLst(2, list2)
+    $$$ repr(l)
+    'ApLst(2, ApLst(1, ApLst()))'
+    $$$ str(l)
+    '[2, 1]'
+    $$$ repr(list2)
+    'ApLst(1, ApLst())'
+    """
+    
+    def __init__(self, *args):
+        """
+        build a new empty list if args is empty, 
+        or a list whose head is first element of args, 
+        and tail list is second element of args.
+    
+        précondition: len(args) in {0, 2} 
+             and if len(args) == 2, args[1] must be a ApLst
+    
+        raise: `ApLstError` if précondition is not satisfied
+        """
+        if len(args) == 0:
+            self.content = ()
+        elif len(args) == 2:
+            if isinstance(args[1], ApLst):
+                self.content = (args[0], args[1])
+            else:
+                raise ApLstError('bad type for second argument')
+        else:
+            raise ApLstError('bad number of arguments')
+
+    def is_empty(self) -> bool:
+        """
+        return 
+           - True if self is empty
+           - False otherwise
+        précondition: none
+        """
+        return len(self.content) == 0
+
+    def head(self):
+        """
+        return: head element of self
+        raise: `ApLstError` if self is empty
+        """
+        if self.is_empty():
+            raise ApLstError('head: empty list')
+        else:
+            return self.content[0]
+
+    def tail(self) -> "ApLst":
+        """
+        return: tail list of self
+        raise: `ApLstError` if self is empty
+        """
+        if self.is_empty():
+            raise ApLstError('head: empty list')
+        else:
+            return self.content[1]
+        
+    def __str__(self) -> str:
+        """
+        return: a string representation of list self
+        précondition: none
+        """
+        def str_content(self, item_number=0):
+            if self.is_empty():
+                return ''
+            elif item_number == 50:
+                return ', ...'
+            else:
+                comma = '' if item_number == 0 else ', '
+                return (comma + str(self.head()) +
+                        str_content(self.tail(), item_number + 1))
+                
+        return f'[{str_content(self)}]'
+
+    def __repr__(self) -> str:
+        if self.is_empty():
+            content = ""
+        else:
+            content = f"{repr(self.head())}, {repr(self.tail())}"
+        return f"ApLst({content})"
+
+   
+if (__name__ == '__main__'):
+    import apl1test
+    apl1test.testmod("aplst.py")