This project serves as a comprehensive learning resource for building type-safe mobile applications using the ReScript + React Native stack. Through building an interactive math problem solver, you'll learn the core concepts, patterns, and best practices of this powerful technology combination.
What You'll Learn:
- 🎯 ReScript Fundamentals - Type system, pattern matching, and functional programming
- 📱 React Native Development - Cross-platform mobile app development
- ⚛️ ReScript-React - Type-safe React components and state management
- 🧮 Interactive UI Design - Calculator interfaces and user feedback systems
- 🎨 Styling & Theming - React Native StyleSheet and responsive design
- 🏗️ Project Architecture - Modular code organization and component patterns
MathProblemApp/
├── package.json # Dependencies and scripts
├── rescript.json # ReScript compiler configuration
├── App.js # Expo entry point (minimal JS bridge)
├── app.json # Expo configuration
├── assets/ # App icons and images
├── dev.sh # Development helper script
└── src/ # All ReScript source code
├── App.res # 🎯 Main app - Learn navigation & state
├── types/
│ └── MathProblem.res # 🎯 Data modeling - Learn type design
└── components/
├── Calculator.res # 🎯 Interactive UI - Learn complex components
└── MathProblemList.res # 🎯 List rendering - Learn array handling
- Start with
types/MathProblem.res
- Understand ReScript's type system - Study
components/MathProblemList.res
- Learn component patterns - Examine
components/Calculator.res
- Explore state management - Analyze
App.res
- See how components compose together
- Node.js 16+ and npm
- iOS Simulator (Mac) or Android Studio (for mobile testing)
- VS Code with ReScript extension (recommended)
# Install dependencies
npm install
# Start development (compiles ReScript + starts Expo)
npm run web # Run in browser (best for learning)
npm start # Full Expo dev server
npm run ios # iOS simulator
npm run android # Android emulator
# Development commands
npm run res:build # Compile ReScript once
npm run res:watch # Watch mode for development
npm run clean # Reset build cache
ReScript is a strongly-typed functional language that compiles to readable JavaScript. Key learning areas:
// Variant types for exhaustive pattern matching
type difficulty = Easy | Medium | Hard
type attemptStatus = NotAttempted | Correct | Incorrect
// Record types with optional fields
type mathProblem = {
id: option<string>, // option<'a> = Some(value) | None
question: string,
answer: float,
difficulty: difficulty,
status: attemptStatus,
attempts: int,
}
// Exhaustive matching - compiler ensures all cases handled
let getStatusColor = (status: attemptStatus) => {
switch status {
| NotAttempted => "#E3F2FD" // Light blue
| Correct => "#E8F5E8" // Light green
| Incorrect => "#FFE8E8" // Light red
}
}
// Labeled arguments for clear APIs
let create = (~question: string, ~answer: float, ~difficulty: difficulty) => {
// Record creation with type inference
{
id: None,
question: question,
answer: answer,
difficulty: difficulty,
status: NotAttempted,
attempts: 0,
}
}
Key Learning Resources:
📖 React Native Docs | 📖 Expo Docs
React Native enables cross-platform mobile development. Expo simplifies the development workflow:
- No native code setup - Start coding immediately
- Hot reloading - See changes instantly
- Web support - Test in browser during development
- Easy deployment - Build and distribute apps easily
// All imports are typed and checked
open ReactNative
// Component creation with hooks
@react.component
let make = (~problems: array<mathProblem>) => {
let (currentProblem, setCurrentProblem) = React.useState(() => None)
// JSX-like syntax with type safety
<View style={styles["container"]}>
<Text style={styles["title"]}>
{React.string("Math Problems")}
</Text>
<FlatList
data=problems
keyExtractor={(item, _) => /* type-safe key extraction */}
renderItem={({item}) => /* component rendering */}
/>
</View>
}
Key Learning Areas:
Type-safe React bindings for ReScript enable confident component development:
// Component with labeled props
@react.component
let make = (~problem: mathProblem, ~onComplete: (mathProblem, bool) => unit) => {
// Local state with type inference
let (display, setDisplay) = React.useState(() => "0")
let (attempts, setAttempts) = React.useState(() => 0)
// Event handlers with type safety
let handleSubmit = () => {
let userAnswer = Js.Float.fromString(display)
let correct = Js.Math.abs_float(userAnswer -. problem.answer) < 0.001
onComplete(problem, correct)
}
// Conditional rendering
{showResult ?
<ResultView correct={correct} answer={problem.answer} /> :
<CalculatorKeypad onSubmit={handleSubmit} />
}
}
// useState with proper typing
let (state, setState) = React.useState(() => initialValue)
// useEffect for side effects
React.useEffect0(() => {
// Setup code
Some(() => {
// Cleanup function
})
})
Key Learning Resources:
Type-safe bindings for React Native components:
// Type-safe style creation
let styles = StyleSheet.create({
"container": Style.s({
flex: 1.,
backgroundColor: "#f5f5f5",
padding: 20.->Style.dp,
}),
"button": Style.s({
backgroundColor: "#007AFF",
borderRadius: 8.,
padding: 16.->Style.dp,
}),
})
// Style composition
<View style={Style.array([
styles["button"],
isActive ? styles["activeButton"] : Style.empty
])}>
// FlatList with type-safe data
<FlatList
data=problems // array<mathProblem>
keyExtractor={(item, _) =>
switch item.id {
| Some(id) => id
| None => Js.Float.toString(item.createdAt)
}
}
renderItem={({item}) =>
<ProblemCard problem=item onSelect=handleSelect />
}
/>
- Number keypad with 0-9, decimal point, and operations
- Display management with proper input validation
- Submit/Clear functionality with user feedback
- Backspace support for input correction
- Progress tracking - Shows "Attempt X of 3"
- Multiple chances - Encourages learning from mistakes
- Immediate feedback - Correct/incorrect indication
- Answer revelation - Shows solution after max attempts
- Color-coded problems:
- 🔵 Blue: Not attempted yet
- 🟢 Green: Solved correctly
- 🔴 Red: Failed after 3 attempts
- Status indicators - "Tap to solve", "✓ Solved", "✗ Failed"
- Smooth transitions between states
let sampleProblems = [
MathProblem.create(~question="What is 2 + 2?", ~answer=4.0, ~difficulty=Easy),
MathProblem.create(~question="What is 15 × 8?", ~answer=120.0, ~difficulty=Medium),
MathProblem.create(~question="Solve for x: 3x + 7 = 22", ~answer=5.0, ~difficulty=Medium),
MathProblem.create(~question="What is the square root of 144?", ~answer=12.0, ~difficulty=Easy),
MathProblem.create(~question="What is 17² - 13²?", ~answer=120.0, ~difficulty=Hard),
MathProblem.create(~question="If f(x) = 2x + 3, what is f(5)?", ~answer=13.0, ~difficulty=Medium),
]
// App-level state management
let (problems, setProblems) = React.useState(() => sampleProblemsWithIds)
let (currentProblem, setCurrentProblem) = React.useState(() => None)
// Problem completion handler
let handleProblemComplete = (problem: mathProblem, isCorrect: bool) => {
setProblems(prevProblems =>
prevProblems->Js.Array2.map(p =>
p.id == problem.id
? {...p, status: isCorrect ? Correct : Incorrect, attempts: p.attempts + 1}
: p
)
)
}
// Parent-child data flow
<Calculator
problem={problem}
onComplete={handleProblemComplete} // Child to parent communication
onBack={handleBackToList}
/>
// List item interaction
<MathProblemList
problems={problems}
onProblemSelect={handleProblemSelect} // User interaction callback
/>
While not used in this basic app, ReScript Decco is essential for real-world applications handling JSON data:
// Type-safe JSON serialization/deserialization
@decco
type mathProblem = {
id: option<string>,
question: string,
answer: float,
difficulty: difficulty,
status: attemptStatus,
attempts: int,
}
// Auto-generated encode/decode functions
let json = mathProblem_encode(problem)
let problem = mathProblem_decode(json)
When You'll Need Decco:
- API communication with servers
- Local storage persistence
- Data import/export features
- Firebase Firestore integration (see Future Enhancements)
- ReScript - Syntax highlighting, error reporting, auto-completion
- Expo Tools - Expo project management and debugging
- React Native Tools - Debugging and development support
// package.json scripts
{
"res:build": "rescript", // Compile ReScript to JS
"res:watch": "rescript -w", // Watch mode for development
"res:clean": "rescript clean", // Clean compiled files
"clean": "npm run res:clean && rm -rf node_modules/.cache .expo"
}
// Using option types for nullable values
switch problem.id {
| Some(id) => useExistingId(id)
| None => generateNewId()
}
// Result types for operations that can fail
type result<'a, 'b> = Ok('a) | Error('b)
let validateAnswer = (input: string): result<float, string> => {
let num = Js.Float.fromString(input)
Js.Float.isNaN(num) ? Error("Invalid number") : Ok(num)
}
// Reusable styled components
module StyledButton = {
@react.component
let make = (~onPress, ~style=?, ~children) => {
<TouchableOpacity
style={Style.array([
styles["defaultButton"],
style->Belt.Option.getWithDefault(Style.empty)
])}
onPress={onPress}>
{children}
</TouchableOpacity>
}
}
// Usage with type safety
<StyledButton onPress={handleSubmit} style={styles["primaryButton"]}>
<Text> {React.string("Submit Answer")} </Text>
</StyledButton>
// Functional array transformations
let completedProblems = problems
->Js.Array2.filter(p => p.status == Correct)
->Js.Array2.map(p => p.question)
// Safe array access
let firstProblem = problems->Belt.Array.get(0) // option<mathProblem>
- Add new sample problems - Practice record creation and array operations
- Modify difficulty colors - Learn styling and pattern matching
- Adjust attempt limits - Understand component props and state
- Add problem categories - Extend type definitions and filtering
- Implement timer functionality - Practice useEffect and time management
- Create hint system - Build progressive disclosure UI patterns
- Add data persistence - Integrate localStorage with ReScript Decco
- Implement score calculation - Complex state transformations
- Build problem generator - Algorithmic problem creation
📖 Firebase Docs | 📖 ReScript Firebase
Transform this local app into a collaborative learning platform:
// Firebase Firestore integration with ReScript
module FirebaseService = {
// Real-time problem synchronization
let subscribeToProblemUpdates = (callback: array<mathProblem> => unit) => {
// Subscribe to Firestore collection changes
// Auto-sync problems across devices
// Handle offline/online state
}
// Collaborative features
let shareProgress = (userId: string, problemId: string, attempts: int) => {
// Share solving progress with teachers/parents
// Track learning analytics
// Enable classroom competitions
}
}
Firebase Integration Benefits:
- Real-time sync - Problems update across all devices instantly
- User authentication - Personal progress tracking
- Collaborative learning - Share problems between classrooms
- Analytics - Track learning progress and difficulty areas
- Offline support - Works without internet, syncs when connected
Implementation Path:
- Set up Firebase project and Firestore database
- Add Firebase SDK and ReScript bindings
- Create service layer for data operations
- Implement real-time listeners with ReScript subscriptions
- Add user authentication and personal progress tracking
- Build teacher dashboard for classroom management
- Compilation errors - Read error messages carefully, they're very helpful
- Type mismatches - Use type annotations to clarify intent
- Missing record fields - Ensure all required fields are provided
- Pattern matching - Handle all variant cases for exhaustiveness
- Hot reloading - Shake device or Cmd+R to reload
- Metro bundler - Clear cache with
npm start -- --clear
- Simulator issues - Reset simulator if styling looks wrong
- Web vs mobile - Some features work differently on web
- Use FlatList for long lists instead of ScrollView
- Optimize re-renders with React.memo when needed
- Minimize state updates - batch updates when possible
- Profile with Flipper for advanced debugging
- ReScript Language - Complete language guide
- ReScript-React - React bindings documentation
- React Native - Mobile development guide
- Expo - Development platform documentation
- rescript-react-native - React Native bindings
- ReScript Decco - JSON serialization
- Belt Standard Library - Utility functions
- ReScript Firebase - Firebase integration
- ReScript Forum - Community discussions
- ReScript Discord - Real-time help
- GitHub Examples - Sample projects
- Awesome ReScript - Curated resources
- Immediate feedback - Compile-time error checking prevents runtime bugs
- Clear architecture - Functional programming promotes clean, maintainable code
- Progressive complexity - Start simple, add features incrementally
- Industry relevance - Skills transfer to web development and other platforms
- Type safety - Catch errors before they reach users
- Performance - React Native provides near-native performance
- Cross-platform - Single codebase for iOS, Android, and web
- Maintainability - ReScript's type system makes refactoring safe and easy
- Ecosystem - Access to entire npm ecosystem with type safety
Ready to master ReScript + React Native? Start exploring the code and building amazing mobile apps! 🚀📱# Testing pre-commit hook