пятница, 10 марта 2017 г.

What I really need in C#

  1. Switches without breaks (there is compilation error now, so I think they can put breaks implicitly)
  2. Multiple yielding using yield! (why is it not there yet?)
  3. Non nullable reference types by default (got bored of all this asserts, contracts and helper classes)

четверг, 12 января 2017 г.

Existential types workaround in C#

I need to create a generic tree, where children nodes can have a different type.
class Node<T>
{
   public T Value { get; }
   public Node<?>[] Children { get; }

   public Node(T value, Node<?>[] children)
   {
       Value = value;
       Children = children;
   }
}
And a simple depth calculator.
public static int Depth(Node<?> node) =>
   1 + node.Children.Select(Depth)
                    .DefaultIfEmpty().Max();
But what can I put instead of wildcard '?' in C# ?
It cannot be 'object' because I want to restrict children of the same parent to have the same type.
It cannot be 'T' or 'T2' because different nested levels may have different types.
Type information is not used in Depth method, so I can use existential types here. But, unfortunately, C# doesn't support them.

This answer on StackOverflow suggested a hint.
∃'x.T<'x> ≡ ∀'z.(∀'x.T<'x> -> 'z) -> 'z
I can simulate existential type using two universal types. They both hide children type from the parent class.
interface IExistentialList
{
    TRet Apply<TRet>(IExistentialFunc<TRet> a);
}

interface IExistentialFunc<TRet>
{
    TRet Apply<T>(Node<T>[] a);
}
Children's type will be stored in implementation class.
class SpecificList<T> : IExistentialList
{
    private Node<T>[] nodes;
 
    public SpecificList(Node<T>[] nodes)
    {
        this.nodes = nodes;
    }
 
    public TRet Apply<TRet>(IExistentialFunc<TRet> a) => a.Apply(nodes);
}
Tree will become.
class Node<T>
{
 public T Value { get; }
 public IExistentialList Children { get; }

 public Node(T value, IExistentialList children)
 {
  Value = value;
  Children = children;
 }
}
Depth function will become a class.
class DepthFunc : IExistentialFunc<int>
{
    public static int Invoke<T>(Node<T> s) =>
        new DepthFunc().Run(s);

    private int Run<T>(Node<T> s) =>
        1 + s.Children.Apply(this);

    public int Apply<T>(Node<T>[] list) =>
        list.Select(Run).DefaultIfEmpty(0).Max();
}
I can use it easily with some helper functions.
public static Node<TP> Create<TPTC>(TP value, params Node<TC>[] array) =>
    new Node<TP>(value, new SpecificList<TC>(array));

public static Node<TP> Create<TP>(TP value) =>
    new Node<TP>(value, new SpecificList<object>(new Node<object>[0]));
 
public static void Main()
{
    var tree = Create(1,
        Create("test1", Create(10.5f)),
        Create("test2"));

    Console.WriteLine(DepthFunc.Invoke(tree));
}

вторник, 6 сентября 2016 г.

Query provider written in F#

Query provider written in F#

Questions so far:

  1. Is there a better more fsharpish way? I tried to use DU here, but stuck with explicit generic type annotation in query function for return value. The F# is trying to infer the first matching type with similar properties based on further use, which sometimes breaks the runtime casting after adding one more Dto record. (very weird)
  2. How can I get rid of this 'cast' function in every case?I tried :> for the error: 'The static coercion from type AccountDto to 'T involves an indeterminate type based on information prior to this program point. Static coercions are not allowed on some types. Further type annotations are needed.'
    I tried :?> for the error: 'The type 'AccountDto' does not have any proper subtypes and cannot be used as the source of a type test or runtime coercion.'
  3. I've decorated every record with RequiredQualifiedAccess attribute 'cause I can't live with the ambiguous type inference for records with similar properties. Am I the only one?
  4. To fight this ambiguity I've created single case unions for every record I use. Which is not so cool at all.

Source code:

Database:

[<RequireQualifiedAccess>]
type AccountDto = 
    { Id : string
      Amount : Money }

[<RequireQualifiedAccess>]
type PurchaseDto = 
    { Id : string
      ShopId : string }

type MemoryDatabase() = 
    let accounts = new ResizeArray<AccountDto>();
    let purchases = new ResizeArray<PurchaseDto>();
    member __.Accounts = accounts
    member __.Purchases = purchases

Query specs:

type IQuerySpec<'T> = interface end 

[<RequireQualifiedAccess>]
type AccountQuerySpecData =
    { AccountId : string }

type AccountQuerySpec = AccountQuerySpec of AccountQuerySpecData interface IQuerySpec<AccountDto
[<RequireQualifiedAccess>]
type PurchaseQuerySpecData =
    { PurchaseId : string }

type PurchaseQuerySpec = PurchaseQuerySpec of PurchaseQuerySpecData interface IQuerySpec<PurchaseDto
[<RequireQualifiedAccess>]
type AccountListQuerySpec() = class
    interface IQuerySpec<AccountDto list>
    end

Query executors:

let accountQuery (AccountQuerySpec spec) (db : MemoryDatabase) =
    db.Accounts |> Seq.find (fun a -> a.Id = spec.AccountId)

let accountListQuery (db : MemoryDatabase) : AccountDto list =
    List.ofSeq db.Accounts

let purchaseQuery (PurchaseQuerySpec spec) (db : MemoryDatabase) = 
    db.Purchases |> Seq.find (fun a -> a.Id = spec.PurchaseId)

let inline cast<'F, 'T> (a : 'F) = a :> obj :?> 'T

let executeQuery<'T> (spec: IQuerySpec<'T>) db : 'T =
    match box spec with
    | :? AccountQuerySpec as spec -> accountQuery spec db |> cast
    | :? AccountListQuerySpec -> accountListQuery db |> cast
    | :? PurchaseQuerySpec as spec -> purchaseQuery spec db |> cast
    | _ -> failwith "Query spec not supported"

Usage:

let db = new MemoryDatabase()
let inline query q = executeQuery q db

let account = query (AccountQuerySpec { AccountId = "home" })
printf "%A\n" account.Amount

let purchase = query (PurchaseQuerySpec { PurchaseId = "operation-1" })
printf "%A\n" purchase.ShopId

let accounts = query (new AccountListQuerySpec())
accounts |> List.iter (fun a -> printf "%A = %A\n" a.Id a.Amount)

суббота, 21 мая 2016 г.

вторник, 1 сентября 2015 г.

Visual Studio Single File Generator to run roslyn Syntax Walker and generate additional members

I've created a custom tool for Visual Studio that runs your custom roslyn Syntax Walker to generate additional members. This is my way for emulating the lack of metaprogramming using roslyn in C#.

https://github.com/xiety/RoslynGenerator

For example, I've used it for generating Identity Value Types:

In AccountId.cs file:

[MetaIdentity("account")]
public partial struct AccountId : IIdentity
{
}

Assign RoslynGenerator as Custom Tool for this file, and it will generate AccountId.get.cs file for you with some boilerplate code:


public partial struct AccountId
{
        private const string Prefix = "account-";
        private readonly string _value;

        public AccountId(string value){ _value = value; }

        public AccountId(long value)
                : this(Prefix + value){}

        public override bool Equals(object obj)
        {
            if (obj == null || obj.GetType() != typeof(AccountId)) return false;
            return _value == ((AccountId)obj)._value;
        }

        public override string ToString() => _value;
        public static implicit operator AccountId(string value) => new AccountId(value);
        public static implicit operator string (AccountId id) => id._value;
        public static bool operator ==(AccountId a, AccountId b) => a._value == b._value;
        public static bool operator !=(AccountId a, AccountId b) => !(a == b);
        public override int GetHashCode() => _value.GetHashCode();
}

To make this transformation RoslynGenerator finds in your project references class called MetaSyntaxWalker and runs it's Generate method dynamically passing SemanticModel and SyntaxRoot of current file.

понедельник, 31 августа 2015 г.

Visual Studio Extension to run all custom tools for all solution files on every build

I've created an extension to Visual Studio that runs all custom tools on every build.

https://github.com/xiety/RunCustomToolsOnBuild

Lessons learned:

1. Make sure that "Copy local" property is set to "False" on every project reference. Or extension would not be installed. Without any messages showed.

2. If breakpoints do not work, you must change Project property "Include Debug Symbols in Local Deployment" to "True"

3. If you get error VSSDK1031: Extension '...' could not be found. Try to reset Exp profile and restart VS.
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe" /RootSuffix Exp /setup

4. Check extension files after VS project build in:
c:\users\{user}\appdata\local\microsoft\visualstudio\14.0exp\extensions\

среда, 17 июня 2015 г.

Prevent implicit casting from null keyword to struct when implicit conversion from string is defined

If your struct has defined implicit conversion from string, then null keyword will be implicitly handled as string and there will be no compiler error.

To prevent this you could define second implicit conversion from any other reference type. Then implicit casting from null keyword becomes ambiguous and will produce error:

Argument type 'null' is not assignable to parameter type 'MyType'

Exactly what we wanted.

вторник, 9 ноября 2010 г.

C# custom tool file generation based on C# template file

Download sources and vsix from CompileGenerator.Codeplex.com

C# custom tool file generation based on C# template file. Write C# Code wich will be converted to another more detailed, specific C# code.
Features
- Build-in VS2010 .cs file Syntax highlighting and IntelliSense in template
- No need to learn new language nor syntax for template writers. C# your best friend here.
- Modifying generation code without restarting VS (code compiled in another domain)
- Code that writes code, it’s fun
- Preprocessed t4 templates for code generator
- No XML
- No more Dependency Property mess in your code
How it’s work
1. Add a new simple .cs file into your project

2. Write code that may describe some other code you need right here in your project (Dependency Properties, DTO or CQRS Commands for example)

using Xiety.DtoGenerator;

public class Nodes : DtoData
{
    public void Run()
    {
        Namespace("Xiety.GenerationSample");

        Class("TableNode""QueryNode")
            .Property("string""TableName");

        Class("OnNode")
            .ListProperty("ExpressionNode""Expressions");

        Class("QueryNode", abstr: true)
            .Property("QueryNode""Query")
            .Property("String""Alias");

        Class("ExpressionNode")
            .Property("string""Content");

        Class("JoinNode""QueryNode")
            .Property("OnNode""On")
            .Property("QueryNode""Left")
            .Property("QueryNode""Right");

        Class("FilterNode""QueryNode")
            .Property("WhereNode""Where");

        Class("WhereNode")
            .ListProperty("ExpressionNode""Expressions");
    }
}
3. Change custom tool settings of your file

4. Save your file and immediately get the new .cs file right in place with generated results

using System;
using System.Collections.Generic;

namespace Xiety.GenerationSample
{
    public class TableNode : QueryNode
    {
        public string TableName { getset; }

    }

    public class OnNode
    {
        public IList<ExpressionNode> Expressions { getset; }

        public OnNode()
        {
            Expressions = new List<ExpressionNode>();
        }
    }

    public abstract class QueryNode
    {
        public QueryNode Query { getset; }
        public String Alias { getset; }

    }

    public class ExpressionNode
    {
        public string Content { getset; }

    }

    public class JoinNode : QueryNode
    {
        public OnNode On { getset; }
        public QueryNode Left { getset; }
        public QueryNode Right { getset; }

    }

    public class FilterNode : QueryNode
    {
        public WhereNode Where { getset; }

    }

    public class WhereNode
    {
        public IList<ExpressionNode> Expressions { getset; }

        public WhereNode()
        {
            Expressions = new List<ExpressionNode>();
        }
    }

}

5. Tune your generator template and code to produce different results
<#@ template language="C#" #>
<#@ import namespace="System.Linq" #>
using System;
using System.Collections.Generic;

<# if (NotEmpty(Data.FileNamespace)) { #>
namespace 
<#= Data.FileNamespace #>
{
<# } #>
<# foreach (var cls in Data.Classes) { #>
    public
<#= If(cls.Abstract, " abstract"#> class <#= cls.Name #><#= IfNotNull(cls.Extends, " : {0}"#>
    {
<# foreach (var property in cls.Properties) { #>
        public 
<#= property.TypeName #> <#= property.Name #> { get; set; }
<# } #>
<# foreach (var property in cls.ListProperties) { #>
        public IList<
<#= property.TypeName #><#= property.Name #> { get; set; }
<# } #>

<# if (cls.ListProperties.Any()) { #>
        public 
<#= cls.Name #>()
        {
<# foreach (var property in cls.ListProperties) { #>
            
<#= property.Name #> = new List<<#= property.TypeName #>>();
<# } #>
        }
<# }//constructor #>
    }

<# } #>
<# if (NotEmpty(Data.FileNamespace)) { #>
}
<# } #>

C# DSL Language to describe template

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace Xiety.DtoGenerator
{
    public class DtoData
    {
        public string FileNamespace { getset; }
        public List<ClassNode> Classes { getset; }

        public DtoData()
        {
            Classes = new List<ClassNode>();
        }

        public void Namespace(string nspace)
        {
            this.FileNamespace = nspace;
        }

        public ClassNode Class(string name, string extends = nullbool abstr = false)
        {

            var cls = new ClassNode(name, extends, abstr);
            this.Classes.Add(cls);
            return cls;
        }
    }

    public class ClassNode
    {
        public bool Abstract { getset; }
        public string Extends { getset; }
        public string Name { getset; }

        public List<PropertyNode> Properties { getset; }
        public List<ListPropertyNode> ListProperties { getset; }

        public ClassNode()
        {
            this.Properties = new List<PropertyNode>();
            this.ListProperties = new List<ListPropertyNode>();
        }

        public ClassNode(string name, string extends, bool abstr)
            : this()
        {

            this.Name = name;
            this.Extends = extends;
            this.Abstract = abstr;
        }

        public ClassNode Property(string type, string name)
        {

            var property = new PropertyNode(type, name);
            this.Properties.Add(property);
            return this;
        }

        public ClassNode ListProperty(string type, string name)
        {

            var property = new ListPropertyNode(type, name);
            this.ListProperties.Add(property);
            return this;
        }
    }

    public class PropertyNode
    {
        public string TypeName { getset; }
        public string Name { getset; }

        public PropertyNode(string typeName, string name)
        {

            this.TypeName = typeName;
            this.Name = name;
        }
    }

    public class ListPropertyNode
    {
        public string TypeName { getset; }
        public string Name { getset; }

        public ListPropertyNode(string typeName, string name)
        {

            this.TypeName = typeName;
            this.Name = name;
        }
    }
}

Download sources and vsix from CompileGenerator.Codeplex.com