title: How can I DRY out this F# code? (Fluent Interface)
body: <p>So this is some of the wettest code I've ever written. But it's useful, which is annoying. The reason for all the repetition is because I want to keep the interface fluent. If I augmented the base class (which happens to be <code>View</code> in this case), it would only give back an instance of <code>View</code>, which would prevent me from doing something like</p>
  
  <pre><code>let label = theme.CreateLabel().WithMargin(new Thickness(5.0)).WithText("Hello")
  </code></pre>
  
  <p>because the <code>Label.Text</code> property is not implemented by the <code>View</code> base class.</p>
  
  <p>So here is my fluent interface. Get ready. It's ugly, and repetitive. But it also works, and is convenient to use.</p>
  
  <p>Have I missed an obvious way to DRY it out?</p>
  
  <pre><code>module ViewExtensions =
      let private withTwoWayBinding&lt;'TElement, 'TProperty, 'TViewModel, 'TView when 'TView :&gt; IViewFor&lt;'TViewModel&gt;&gt;(viewModel: 'TViewModel, view: 'TView, viewModelProperty: Expr&lt;'TViewModel -&gt; 'TProperty&gt;, viewProperty: Expr&lt;'TView -&gt; 'TProperty&gt;) (element: 'TElement) = 
          view.Bind(viewModel, ExpressionConversion.toLinq viewModelProperty, ExpressionConversion.toLinq viewProperty) |&gt; ignore
          element
      let private withHorizontalOptions&lt;'TElement when 'TElement :&gt; View&gt; options (element: 'TElement) =
          element.HorizontalOptions &lt;- options
          element
      let private withVerticalOptions&lt;'TElement when 'TElement :&gt; View&gt; options (element: 'TElement) =
          element.VerticalOptions &lt;- options
          element
      let private withAlignment&lt;'TElement when 'TElement :&gt; View&gt; horizontalOptions verticalOptions (control: 'TElement) =
          control |&gt; withHorizontalOptions horizontalOptions |&gt; withVerticalOptions verticalOptions
      let private withMargin&lt;'TElement when 'TElement :&gt; View&gt; margin (element: 'TElement) = 
          element.Margin &lt;- margin
          element
      let private withActions&lt;'TElement&gt; (actions: ('TElement -&gt; unit)[]) (element: 'TElement) = 
          for action in actions do action(element)
          element
      type Xamarin.Forms.Entry with 
          member this.WithHorizontalOptions(options) = withHorizontalOptions options this
          member this.WithVerticalOptions(options) = withHorizontalOptions options this
          member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this
          member this.WithTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) = withTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) this
          member this.WithMargin(margin) = withMargin margin this
          member this.With(actions) = withActions actions this
          member this.With(action: Entry -&gt; unit) = this.With([|action|])
      type Xamarin.Forms.Grid with 
          member this.WithHorizontalOptions(options) = withHorizontalOptions options this
          member this.WithVerticalOptions(options) = withHorizontalOptions options this
          member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this
          member this.WithMargin(margin) = withMargin margin this
          member this.With(actions) = withActions actions this
          member this.With(action: Grid -&gt; unit) = this.With([|action|])
      type Xamarin.Forms.StackLayout with 
          member this.WithHorizontalOptions(options) = withHorizontalOptions options this
          member this.WithVerticalOptions(options) = withHorizontalOptions options this
          member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this
          member this.WithMargin(margin) = withMargin margin this
          member this.With(actions) = withActions actions this
          member this.With(action: StackLayout -&gt; unit) = this.With([|action|])
      type Xamarin.Forms.Button with 
          member this.WithHorizontalOptions(options) = withHorizontalOptions options this
          member this.WithVerticalOptions(options) = withHorizontalOptions options this
          member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this
          member this.WithMargin(margin) = withMargin margin this
          member this.WithText(text) = this.Text &lt;- text; this
          member this.With(actions) = withActions actions this
          member this.With(action: Button -&gt; unit) = this.With([|action|])
      type Xamarin.Forms.Switch with 
          member this.WithHorizontalOptions(options) = withHorizontalOptions options this
          member this.WithVerticalOptions(options) = withHorizontalOptions options this
          member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this
          member this.WithTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) = withTwoWayBinding(viewModel, view, viewModelProperty, viewProperty) this
          member this.WithMargin(margin) = withMargin margin this
          member this.With(actions) = withActions actions this
          member this.With(action: Switch -&gt; unit) = this.With([|action|])
      type Xamarin.Forms.Label with 
          member this.WithHorizontalOptions(options) = withHorizontalOptions options this
          member this.WithVerticalOptions(options) = withHorizontalOptions options this
          member this.WithAlignment(horizontalOptions, verticalOptions) = withAlignment horizontalOptions verticalOptions this
          member this.WithMargin(margin) = withMargin margin this
          member this.WithText(text) = this.Text &lt;- text; this
          member this.With(actions) = withActions actions this
          member this.With(action: Label -&gt; unit) = this.With([|action|])
  </code></pre>
  
  <p><strong>UPDATE</strong></p>
  
  <p>So thanks to your help, the answer is yes, I was missing something obvious. As TheQuickBrownFox explained, if I change the fluent interface to something of the form</p>
  
  <pre><code>let label = theme.CreateLabel() |&gt; withMargin(new Thickness(5.0)) |&gt; withContent("Hello")
  </code></pre>
  
  <p>then the monster you see above can be replaced in its entirety by</p>
  
  <pre><code>module ViewExtensions =
      let withTwoWayBinding&lt;'TElement, 'TProperty, 'TViewModel, 'TView when 'TView :&gt; IViewFor&lt;'TViewModel&gt;&gt;(viewModel: 'TViewModel, view: 'TView, viewModelProperty: Expr&lt;'TViewModel -&gt; 'TProperty&gt;, viewProperty: Expr&lt;'TView -&gt; 'TProperty&gt;) (element: 'TElement) = 
          view.Bind(viewModel, ExpressionConversion.toLinq viewModelProperty, ExpressionConversion.toLinq viewProperty) |&gt; ignore
          element
      let withHorizontalOptions options (element: #View) = element.HorizontalOptions &lt;- options; element
      let withVerticalOptions options (element: #View) = element.VerticalOptions &lt;- options; element
      let withAlignment horizontalOptions verticalOptions element = element |&gt; withHorizontalOptions horizontalOptions |&gt; withVerticalOptions verticalOptions
      let withMargin margin (element: #View) = element.Margin &lt;- margin; element
      let withCaption text (element: #Button) = element.Text &lt;- text; element
      let withText text (element: #Entry) = element.Text &lt;- text; element
      let withContent text (element: #Label) = element.Text &lt;- text; element
      let withSetUpActions&lt;'TElement&gt; (actions: ('TElement -&gt; unit)[]) (element: 'TElement) = (for action in actions do action(element)); element
      let withSetUpAction&lt;'TElement&gt; (action: 'TElement -&gt; unit) = withSetUpActions([|action|])
  </code></pre>
  
  <p>This code deletion is very pleasing indeed.</p>