added fpdart demo

This commit is contained in:
Jean Jacques Avril 2024-11-27 15:59:08 +01:00
parent 89e12bdf9f
commit caf6007c26
No known key found for this signature in database
10 changed files with 755 additions and 254 deletions

View File

@ -1,254 +0,0 @@
**Vergleich und Vorstellung der funktionalen Aspekte von Go und Dart (mit fpdart) sowie Bezug zum Lambda-Kalkül**
---
### **Einführung**
Sowohl **Go** als auch **Dart** sind moderne Programmiersprachen, die hauptsächlich imperativ und objektorientiert ausgerichtet sind. Dennoch unterstützen beide Sprachen funktionale Programmierparadigmen in unterschiedlichem Maße. Die Bibliothek **fpdart** erweitert Dart um fortgeschrittene funktionale Programmierkonzepte. Im Folgenden werden die funktionalen Aspekte von Go und Dart (mit fpdart) verglichen und erläutert, wie diese mit dem **Lambda-Kalkül** in Verbindung stehen.
---
### **Funktionale Programmierung**
**Funktionale Programmierung** ist ein Programmierparadigma, das Funktionen als zentrale Bausteine betrachtet. Es fördert:
- **Unveränderlichkeit**: Daten werden nicht verändert, sondern es werden neue Datenstrukturen erzeugt.
- **Reine Funktionen**: Funktionen ohne Seiteneffekte.
- **Funktionen höherer Ordnung**: Funktionen, die andere Funktionen als Argumente nehmen oder zurückgeben.
---
### **Go und funktionale Programmierung**
**1. Funktionen als erstklassige Objekte**
- **Definition und Verwendung**: In Go können Funktionen wie Variablen behandelt werden. Sie können zugewiesen, als Parameter übergeben und zurückgegeben werden.
```go
func add(a, b int) int {
return a + b
}
func compute(a int, b int, op func(int, int) int) int {
return op(a, b)
}
result := compute(5, 3, add) // Ergebnis: 8
```
**2. Anonyme Funktionen und Closures**
- **Anonyme Funktionen**: Go unterstützt anonyme Funktionen, die direkt definiert und verwendet werden können.
```go
func main() {
increment := func(x int) int {
return x + 1
}
fmt.Println(increment(5)) // Ausgabe: 6
}
```
- **Closures**: Anonyme Funktionen können auf Variablen aus ihrem umgebenden Kontext zugreifen.
```go
func main() {
x := 0
increment := func() int {
x++
return x
}
fmt.Println(increment()) // Ausgabe: 1
fmt.Println(increment()) // Ausgabe: 2
}
```
**3. Fehlende erweiterte funktionale Konzepte**
- **Keine Immutabilität**: Go fördert nicht standardmäßig unveränderliche Datenstrukturen.
- **Fehlende Funktionen höherer Ordnung im Standardpaket**: Es gibt keine eingebauten Funktionen wie `map`, `filter` oder `reduce` für Slices oder Arrays.
- **Seiteneffekte**: Go erlaubt und verwendet häufig Funktionen mit Seiteneffekten.
---
### **Dart und funktionale Programmierung mit fpdart**
**1. Funktionen als erstklassige Objekte**
- **Definition und Verwendung**: Dart behandelt Funktionen ebenfalls als erstklassige Objekte.
```dart
int add(int a, int b) => a + b;
int compute(int a, int b, int Function(int, int) op) => op(a, b);
var result = compute(5, 3, add); // Ergebnis: 8
```
**2. Anonyme Funktionen und Closures**
- **Anonyme Funktionen (Lambdas)**:
```dart
var increment = (int x) => x + 1;
print(increment(5)); // Ausgabe: 6
```
- **Closures**: Zugriff auf Variablen aus dem umgebenden Kontext.
```dart
void main() {
var x = 0;
var increment = () {
x++;
return x;
};
print(increment()); // Ausgabe: 1
print(increment()); // Ausgabe: 2
}
```
**3. fpdart-Bibliothek**
- **Überblick**:
- **fpdart** ist eine funktionale Programmierbibliothek für Dart, inspiriert von Haskell und anderen funktionalen Sprachen.
- Sie bietet Werkzeuge und Datenstrukturen für funktionale Programmierkonzepte wie **Option**, **Either**, **Monad**, **Functor** usw.
- **Hauptmerkmale**:
- **Option (Maybe)**: Umgang mit optionalen Werten, die vorhanden sein können oder nicht.
```dart
Option<int> parseInt(String str) {
final value = int.tryParse(str);
return value != null ? Some(value) : None<int>();
}
```
- **Either**: Behandlung von Berechnungen, die entweder einen Wert oder einen Fehler liefern.
```dart
Either<String, int> divide(int a, int b) {
return b == 0 ? Left('Division durch Null') : Right(a ~/ b);
}
```
- **Functor und Monad**: Unterstützung für Funktionstransformationen und Sequenzierung von Berechnungen.
- **Vorteile**:
- **Immutabilität**: Fördert unveränderliche Datenstrukturen.
- **Reine Funktionen**: Ermutigt zur Verwendung von Funktionen ohne Seiteneffekte.
- **Typensicherheit**: Durch generische Typen und klare Strukturen werden Laufzeitfehler reduziert.
---
### **Vergleich zwischen Go und Dart (mit fpdart)**
**1. Sprachdesign und Paradigmen**
- **Go**:
- Primär eine imperative Sprache mit einigen funktionalen Merkmalen.
- Legt Wert auf Einfachheit, Leistung und Parallelität.
- Funktionale Programmierung ist möglich, aber nicht im Fokus.
- **Dart (mit fpdart)**:
- Unterstützt objektorientierte und funktionale Programmierung.
- Durch fpdart werden fortgeschrittene funktionale Konzepte zugänglich.
- Ideal für Anwendungen, die von funktionaler Programmierung profitieren, z.B. bei der Entwicklung mit Flutter.
**2. Funktionale Features**
- **Go**:
- Funktionen als Werte, anonyme Funktionen, Closures.
- Keine eingebauten funktionalen Datenstrukturen oder Monaden.
- Keine direkte Unterstützung für Immutabilität oder reine Funktionen.
- **Dart (mit fpdart)**:
- Erweiterte funktionale Bibliothek mit Monaden, Funktoren, Option, Either usw.
- Förderung von Immutabilität und reinen Funktionen.
- Bessere Unterstützung für funktionale Programmiermuster.
**3. Community und Ökosystem**
- **Go**:
- Starkes Ökosystem für serverseitige Anwendungen und Systemprogrammierung.
- Funktionale Programmierung wird weniger betont, aber es gibt Drittanbieterbibliotheken.
- **Dart (mit fpdart)**:
- Wachsende Community, insbesondere durch Flutter.
- fpdart ist aktiv entwickelt und erweitert die Möglichkeiten von Dart in Richtung funktionaler Programmierung.
---
### **Bezug zum Lambda-Kalkül**
**1. Was ist das Lambda-Kalkül?**
- **Lambda-Kalkül** ist ein formales System aus der mathematischen Logik zur Untersuchung von Funktionen und deren Anwendung.
- Es bildet die theoretische Grundlage für die funktionale Programmierung.
- Zentral sind anonyme Funktionen (Lambdas) und die Auswertung durch Funktionsanwendung.
**2. Anwendung in Go und Dart**
- **Anonyme Funktionen**:
- Beide Sprachen unterstützen anonyme Funktionen, die direkt aus dem Lambda-Kalkül abgeleitet sind.
- Diese ermöglichen Funktionen ohne Namen, die wie Variablen behandelt werden können.
- **Funktionen höherer Ordnung**:
- Funktionen, die andere Funktionen als Argumente nehmen oder zurückgeben.
- Wesentlich für die Ausdruckskraft des Lambda-Kalküls und in beiden Sprachen verfügbar.
**3. Erweiterte Konzepte mit fpdart**
- **Monaden und Funktoren**:
- Konzepte aus der Kategorie-Theorie, eng verwandt mit dem Lambda-Kalkül.
- Erlauben das Sequenzieren und Komponieren von Berechnungen auf abstrakte Weise.
- fpdart implementiert diese Konzepte in Dart und erweitert somit die Ausdruckskraft der Sprache im Sinne des Lambda-Kalküls.
**4. Relevanz**
- **Go**:
- Unterstützt grundlegende Konzepte des Lambda-Kalküls, aber ohne tiefe Integration.
- Eignet sich für pragmatische Anwendungen, bei denen funktionale Programmierung nicht im Vordergrund steht.
- **Dart (mit fpdart)**:
- Ermöglicht die Anwendung komplexerer Konzepte des Lambda-Kalküls.
- Bietet eine reichhaltigere Plattform für funktionale Programmierung.
---
### **Fazit**
- **Go** bietet grundlegende funktionale Programmiermöglichkeiten, aber der Schwerpunkt liegt auf imperativer und prozeduraler Programmierung.
- **Dart**, insbesondere mit der **fpdart**-Bibliothek, bietet erweiterte Unterstützung für funktionale Programmierung und ermöglicht die Nutzung fortgeschrittener Konzepte wie Monaden, Funktoren und unveränderliche Datenstrukturen.
- **Lambda-Kalkül** bildet die theoretische Basis für funktionale Programmierung und ist in beiden Sprachen durch anonyme Funktionen und Funktionen höherer Ordnung präsent. Mit fpdart wird Dart jedoch stärker an die Prinzipien des Lambda-Kalküls angelehnt.
---
### **Empfehlung**
Wenn Sie an funktionaler Programmierung interessiert sind und diese Konzepte in Ihrer Arbeit einsetzen möchten, ist **Dart mit fpdart** eine geeignete Wahl. Es bietet eine umfangreiche Bibliothek, um funktionale Paradigmen effektiv zu nutzen. **Go** ist hingegen ideal, wenn Sie eine einfache und performante Sprache für systemnahe Anwendungen benötigen, bei denen funktionale Programmierung nicht im Mittelpunkt steht.
---
### **Weiterführende Ressourcen**
- **fpdart GitHub-Repository**: [https://github.com/SandroMaglione/fpdart](https://github.com/SandroMaglione/fpdart)
- **Offizielle Go-Dokumentation**: [https://golang.org/doc/](https://golang.org/doc/)
- **Dart Programmierung**: [https://dart.dev/](https://dart.dev/)
- **Einführung in funktionale Programmierung und Lambda-Kalkül**:
- **Buch**: "Funktionale Programmierung in Scala" (auch für andere Sprachen hilfreich)
- **Online-Ressourcen**: Suchen Sie nach Tutorials zu funktionaler Programmierung und Lambda-Kalkül.

View File

@ -0,0 +1,3 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

View File

@ -0,0 +1 @@
none

View File

@ -0,0 +1,44 @@
# **🚀 Demo-Projekt: AppError und Funktionale Programmierung mit fpdart**
Dieses Demo-Projekt begleitet die Zwischenpräsentation und verdeutlicht, wie funktionale Programmierung mit Dart und der Bibliothek `fpdart` umgesetzt werden kann. Es greift die zentralen Konzepte der Präsentation auf.
Das Projekt zeigt, wie durch funktionale Ansätze mit `fpdart`:
- ✅ Logik deklarativ ausgedrückt wird.
- 🛠️ Fehler elegant und strukturiert behandelt werden.
- 🔄 Erweiterbare, wartbare und typsichere Programme entstehen.
## **🔑 Zentrale Aspekte**
### **1. Monaden als Datenkonstrukte**
- 📦 Anwendung von **`Either`** für synchrone Fehler- und Erfolgsverarbeitung.
- ⏳ Verwendung von **`TaskEither`** zur Kombination von Lazy Evaluation mit asynchroner Verarbeitung und Fehlerbehandlung.
### **2. 🛡️ `AppError`: Einheitliche Fehlerstruktur**
- Einführung eines stark typisierten `AppError`, um Fehler systematisch zu kategorisieren (z. B. `ValidationError`, `DatabaseError`, `NetworkError`).
- Klare Trennung von:
- 📄 **Nachricht** (für Benutzer)
- ⚙️ **Typ** (zur Kategorisierung)
- 🔍 **Details** (für Debugging)
### **3. Praxisnahe Beispiele**
- 🔍 **Validierung**: Prüfung von Benutzereingaben mit `Either`.
- 🛠️ **Fehlerbehandlung**: Nutzung von `TaskEither` für Datenbank- und Netzwerkfehler.
- 🔗 **Verkettung von Funktionen**:
- Nahtlose Kombination von Validierung, Speicherung und weiteren Prozessen.
- Kein Abbruch bei Zwischenfehlern.
### **4. Verbindung zu funktionalen Konzepten**
- 🏛️ **Ursprung**: Konzepte aus Haskell und FP-TS.
- 🌀 **Monaden**: Praktische Anwendung zur sicheren Datenverarbeitung ohne Seiteneffekte.
- 🌐 **Anwendungsbezug**: Funktionale Programmierung als Werkzeug für robuste und skalierbare Software.
Das Projekt dient als praktische Grundlage, um die in der Präsentation behandelten Konzepte von funktionaler Programmierung und Fehlerbehandlung greifbar zu machen. Es zeigt, wie mit `fpdart`:
- Der Code sauber und wartbar bleibt.
- Funktionale Programmierung in Dart effektiv umgesetzt wird.
- Fehlermanagement nicht nur robust, sondern auch elegant sein kann.

View File

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,46 @@
import 'package:fpdart/fpdart.dart';
// Fehlerkategorien
enum AppErrorType {
ValidationError,
DatabaseError,
NetworkError,
UnknownError,
}
class AppError {
final String message; // Benutzerfreundliche Fehlermeldung
final AppErrorType type; // Typisierte Fehlerkategorie
final Exception? exception; // Originale Exception (falls vorhanden)
AppError({
required this.message,
this.type = AppErrorType.UnknownError,
this.exception,
});
@override
String toString() {
return 'AppError(message: $message, type: $type, exception: $exception)';
}
/// Hilfsmethode zur einfachen Erstellung eines `Left`-Werts für `Either`
static Either<AppError, T> left<T>(
String message, {
AppErrorType? type,
Exception? exception,
}) {
return Either.left(AppError(
message: message,
type: type ?? AppErrorType.UnknownError,
exception: exception,
));
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is AppError && other.message == message && other.type == type;
}
}

View File

@ -0,0 +1,68 @@
import 'package:demo/app_error.dart';
import 'package:fpdart/fpdart.dart';
// Mock-Datenbank
final existingEmails = ["existing@example.com"];
// Validierungsfunktionen
Either<AppError, String> validateName(String name) {
return name.isNotEmpty
? Either.right(name)
: AppError.left(
"Name cannot be empty",
type: AppErrorType.ValidationError,
);
}
Either<AppError, String> validateEmail(String email) {
final emailRegex = RegExp(r"^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+");
return emailRegex.hasMatch(email)
? Either.right(email)
: AppError.left(
"Invalid email format",
type: AppErrorType.ValidationError,
);
}
// Asynchrone Überprüfung, ob die E-Mail bereits existiert
TaskEither<AppError, bool> checkEmailExists(String email) {
return TaskEither.tryCatch(
() async => existingEmails.contains(email),
(error, _) => AppError(
message: "Error checking email existence",
type: AppErrorType.DatabaseError,
exception: error as Exception?,
),
);
}
// Benutzer in der Datenbank speichern (Dummy-Implementierung)
TaskEither<AppError, bool> saveUser(String name, String email) {
return TaskEither.tryCatch(
() async {
await Future.delayed(Duration(seconds: 1)); // Simulierte Latenz
print("User saved: $name, $email");
return true;
},
(error, _) => AppError(
message: "Error saving user",
type: AppErrorType.DatabaseError,
exception: error as Exception?,
),
);
}
// Optional: Begrüßungsnachricht senden
TaskEither<AppError, bool> sendWelcomeEmail(String email) {
return TaskEither.tryCatch(
() async {
print("Welcome email sent to $email");
return true;
},
(error, _) => AppError(
message: "Error sending welcome email",
type: AppErrorType.NetworkError,
exception: error as Exception?,
),
);
}

View File

@ -0,0 +1,410 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77"
url: "https://pub.dev"
source: hosted
version: "73.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.2"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a"
url: "https://pub.dev"
source: hosted
version: "6.8.0"
args:
dependency: transitive
description:
name: args
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
url: "https://pub.dev"
source: hosted
version: "2.6.0"
async:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev"
source: hosted
version: "2.12.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
coverage:
dependency: transitive
description:
name: coverage
sha256: "4b03e11f6d5b8f6e5bb5e9f7889a56fe6c5cbe942da5378ea4d4d7f73ef9dfe5"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fpdart:
dependency: "direct main"
description:
name: fpdart
sha256: "1b84ce64453974159f08046f5d05592020d1fcb2099d7fe6ec58da0e7337af77"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360"
url: "https://pub.dev"
source: hosted
version: "4.1.1"
io:
dependency: transitive
description:
name: io
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
js:
dependency: transitive
description:
name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev"
source: hosted
version: "0.7.1"
lints:
dependency: "direct dev"
description:
name: lints
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
url: "https://pub.dev"
source: hosted
version: "0.1.2-main.4"
matcher:
dependency: transitive
description:
name: matcher
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16+1"
meta:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.dev"
source: hosted
version: "1.16.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
node_preamble:
dependency: transitive
description:
name: node_preamble
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
package_config:
dependency: transitive
description:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
url: "https://pub.dev"
source: hosted
version: "1.1.3"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
url: "https://pub.dev"
source: hosted
version: "2.0.1"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
url: "https://pub.dev"
source: hosted
version: "2.1.2"
source_maps:
dependency: transitive
description:
name: source_maps
sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703"
url: "https://pub.dev"
source: hosted
version: "0.10.12"
source_span:
dependency: transitive
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
url: "https://pub.dev"
source: hosted
version: "1.12.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.2"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test:
dependency: "direct dev"
description:
name: test
sha256: f2a018e2baa6fce7c8daa55b8bdf4b3d7d165f82caac269e4cbe5edd666c0e4c
url: "https://pub.dev"
source: hosted
version: "1.25.9"
test_api:
dependency: transitive
description:
name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.dev"
source: hosted
version: "0.7.4"
test_core:
dependency: transitive
description:
name: test_core
sha256: "60ff490bb383858015df7b7a0d883301a426edf9033989f55f091d91efb9dfaf"
url: "https://pub.dev"
source: hosted
version: "0.6.6"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
url: "https://pub.dev"
source: hosted
version: "14.3.1"
watcher:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web:
dependency: transitive
description:
name: web
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.5.4 <4.0.0"

View File

@ -0,0 +1,16 @@
name: demo
description: A sample command-line application.
version: 1.0.0
# repository: https://github.com/my_org/my_repo
environment:
sdk: ^3.5.4
# Add regular dependencies here.
dependencies:
fpdart: ^1.1.1
# path: ^1.8.0
dev_dependencies:
lints: ^4.0.0
test: ^1.24.0

View File

@ -0,0 +1,137 @@
import 'package:demo/app_error.dart';
import 'package:demo/demo.dart';
import 'package:fpdart/fpdart.dart';
import 'package:test/test.dart';
void main() {
group("User Registration Tests", () {
test("Valid name and email results in successful registration", () async {
final name = "John Doe";
final email = "newuser@example.com";
final result =
await Either<AppError, (String, String)>.right((name, email))
.flatMap((userData) => validateName(userData.$1).flatMap(
(validName) => validateEmail(userData.$2)
.map((validEmail) => (validName, validEmail))))
.toTaskEither()
.flatMap((validUser) => checkEmailExists(validUser.$2).flatMap(
(exists) => exists
? AppError.left("Email already exists",
type: AppErrorType.ValidationError)
.toTaskEither()
: saveUser(validUser.$1, validUser.$2)))
.flatMap((_) => sendWelcomeEmail(email))
.run();
result.match(
(error) => fail("Test failed with error: ${error.message}"),
(success) => expect(success, equals(true)),
);
});
test("Invalid email format returns validation error", () async {
final name = "John Doe";
final email = "invalid-email";
final result =
await Either<AppError, (String, String)>.right((name, email))
.flatMap((userData) => validateName(userData.$1).flatMap(
(validName) => validateEmail(userData.$2)
.map((validEmail) => (validName, validEmail))))
.toTaskEither()
.run();
result.match(
(error) {
expect(error.message, equals("Invalid email format"));
expect(error.type, equals(AppErrorType.ValidationError));
},
(success) => fail("Test succeeded unexpectedly"),
);
});
test("Existing email results in error", () async {
final name = "John Doe";
final email = "existing@example.com";
final result =
await Either<AppError, (String, String)>.right((name, email))
.flatMap((userData) => validateName(userData.$1).flatMap(
(validName) => validateEmail(userData.$2)
.map((validEmail) => (validName, validEmail))))
.toTaskEither()
.flatMap((validUser) => checkEmailExists(validUser.$2).flatMap(
(exists) => exists
? AppError.left("Email already exists",
type: AppErrorType.ValidationError)
.toTaskEither()
: saveUser(validUser.$1, validUser.$2)))
.run();
result.match(
(error) => expect(error.message, equals("Email already exists")),
(success) => fail("Test succeeded unexpectedly"),
);
});
test("Empty name returns validation error", () async {
final name = "";
final email = "newuser@example.com";
final result =
await Either<AppError, (String, String)>.right((name, email))
.flatMap((userData) => validateName(userData.$1).flatMap(
(validName) => validateEmail(userData.$2)
.map((validEmail) => (validName, validEmail))))
.toTaskEither()
.run();
result.match(
(error) => expect(
error,
equals(AppError(
message: "Name cannot be empty",
type: AppErrorType.ValidationError))),
(success) => fail("Test succeeded unexpectedly"),
);
});
test("Error while sending welcome email is handled gracefully", () async {
final name = "John Doe";
final email = "newuser@example.com";
// Simuliere einen Fehler beim Senden der Begrüßungs-E-Mail
TaskEither<AppError, bool> sendWelcomeEmail(String email) {
return TaskEither.left(AppError(
message: "Error sending welcome email",
type: AppErrorType.NetworkError,
));
}
final result =
await Either<AppError, (String, String)>.right((name, email))
.flatMap((userData) => validateName(userData.$1).flatMap(
(validName) => validateEmail(userData.$2)
.map((validEmail) => (validName, validEmail))))
.toTaskEither()
.flatMap((validUser) =>
checkEmailExists(validUser.$2).flatMap((exists) => exists
? AppError.left(
"Email already exists",
type: AppErrorType.ValidationError,
).toTaskEither()
: saveUser(validUser.$1, validUser.$2)))
.flatMap((_) => sendWelcomeEmail(email))
.run();
result.match(
(error) {
expect(error.message, equals("Error sending welcome email"));
expect(error.type, equals(AppErrorType.NetworkError));
},
(success) => fail("Test succeeded unexpectedly"),
);
});
});
}