In the following code example, we have an class for immutable objects that represents a room. North, South, East, and West represent exits into other rooms.
public sealed class Room { public Room(string name, Room northExit, Room southExit, Room eastExit, Room westExit) { this.Name = name; this.North = northExit; this.South = southExit; this.East = eastExit; this.West = westExit; } public string Name { get; } public Room North { get; } public Room South { get; } public Room East { get; } public Room West { get; } }
So we see, this class is designed with a reflexive circular reference. But because the class immutable, I'm stuck with a 'chicken or the egg' problem. I'm certain that experienced functional programmers know how deal with this. How can it be handled in C#?
I'm endeavoring to code a text-based adventure game, but using functional programming principles just for the sake of learning. I'm stuck on this concept and can use some help!!! Thanks.
UPDATE:
Here's a working implementation based on Mike Nakis' answer regarding lazy initialization:
using System; public sealed class Room { private readonly Func<Room> north; private readonly Func<Room> south; private readonly Func<Room> east; private readonly Func<Room> west; public Room( string name, Func<Room> northExit = null, Func<Room> southExit = null, Func<Room> eastExit = null, Func<Room> westExit = null) { this.Name = name; var dummyDelegate = new Func<Room>(() => { return null; }); this.north = northExit ?? dummyDelegate; this.south = southExit ?? dummyDelegate; this.east = eastExit ?? dummyDelegate; this.west = westExit ?? dummyDelegate; } public string Name { get; } public override string ToString() { return this.Name; } public Room North { get { return this.north(); } } public Room South { get { return this.south(); } } public Room East { get { return this.east(); } } public Room West { get { return this.west(); } } public static void Main(string[] args) { Room kitchen = null; Room library = null; kitchen = new Room( name: "Kitchen", northExit: () => library ); library = new Room( name: "Library", southExit: () => kitchen ); Console.WriteLine( $"The {kitchen} has a northen exit that " + $"leads to the {kitchen.North}."); Console.WriteLine( $"The {library} has a southern exit that " + $"leads to the {library.South}."); Console.ReadKey(); } }
Room
example.type List a = Nil | Cons of a * List a
. And a binary tree:type Tree a = Leaf a | Cons of Tree a * Tree a
. As you can see, they're both self-referential (recursive). Here's how you'd define your room:type Room = Nil | Open of {name: string, south: Room, east: Room, north: Room, west: Room}
.Room
class and aList
are in the Haskell I wrote above.