Category Theory via C# (3) Functor and LINQ to Functors

[LINQ via C# series]

[Category Theory via C# series]

Functor and functor laws

In category theory, functor is a mapping from category to category. Giving category C and D, functor F from category C to D is a structure-preserving morphism from C to D, denoted F: C → D:

image6_thumb

  • F maps objects in C to objects in D, for example, X, Y, Z, … ∈ ob(C) are mapped to F(X), F(Y), F(Z), … ∈ in ob(D)
  • F also maps morphisms in C to morphisms in D, for example, m: X → Y ∈ hom(C) is mapped to morphism F(m): F(X) → F(Y) ∈ hom(D). In this tutorial, to align to C#/.NET terms, this morphism mapping capability of functor is also called “select”. so F(m) is also denoted SelectF(m).

And F must satisfy the following functor laws:

  • Composition preservation: F(m2 ∘ m1) ≡ F(m2) ∘ F(m1), or SelectF(m2 ∘ m1) ≡ SelectF(m2) ∘ SelectF(m1), F maps composition in C to composition in D
  • Identity preservation: F(idX) ≡ idF(X), or SelectF(idX) ≡ idF(X), F maps each identity morphism in C to identity morphism in D
    image3_thumb

Endofunctor

When a functor F’s source category and target category are the same category C, it is called endofunctor, denoted F: C → C. In the DotNet category, there are endofunctors mapping objects (types) and morphisms (functions) in DotNet category to other objects and morphisms in itself. In C#, endofunctor in DotNet can be defined as:

// Cannot be compiled.
public interface IFunctor<TFunctor<>> where TFunctor<> : IFunctor<TFunctor>
{
    Func<TFunctor<TSource>, TFunctor<TResult>> Select<TSource, TResult>(Func<TSource, TResult> selector);
}

In DotNet category, objects are types, so the functor’s type mapping capability is represented by generic type TFunctor<>, which maps type T to another type TFunctor<T>. And in DotNet category, morphisms are functions, so the functor’s function mapping capability is represented by the Select method, which maps function of type TSource –> TResult to another function of type TFunctor<TSource> –> TFunctor<TResult>.

Unfortunately, the above interface cannot be compiled, because C#/.NET do not support higher-kinded polymorphism for types.

Type constructor and higher-kinded type

Kind is the meta type of a type:

  • A concrete type has the simplest kind, denoted *. All non generic types (types without type parameters) are of kind *. Closed generic types (types with concrete type arguments) are also concrete types of kind *.
  • An open generic type definition with type parameter can be viewed as a type constructor, which works like a function. For example,  IEnumerable<> can accept a type of kind * (like int), and return another closed type of kind * (like IEnumerable<int>), so IEnumerable<> is a type constructor, its kind is denoted * –> *; ValueTuple<,> can accept 2 types of kind * (like string and bool), and return another closed type of kind * (like ValueTuple<string, bool>) so ValueTuple<,> is a type constructor, its kind is denoted (*, *) –> *, or * –> * –> * in curried style.

In above IFunctor<TFunctor<>> generic type definition, its type parameter TFunctor<> is an open generic type of kind * –> *. As a result, IFunctor<TFunctor<>> can be viewed as a type constructor, which works like a higher-order function, accepting a TFunctor<> type constructor of kind * –> *, and returning a concrete type of kind *. So  IFunctor<TFunctor<>> is of kind (* –> *) –> *. This is called a higher-kinded type, and not supported by .NET and C# compiler. In another word, C# generic type definition does not support its type parameter to have type parameters. In C#, functor support is implemented by LINQ query comprehensions instead of type system.

LINQ to Functors

Built-in IEnumerable<> functor

IEnumerable<> is a built-in functor of DotNet category, which can be viewed as virtually implementing above IFunctor<TFunctor<>> interface:

public interface IEnumerable<T> : IFunctor<IEnumerable<>>, IEnumerable
{
    // Func<IEnumerable<TSource>, IEnumerable<TResult>> Select<TSource, TResult>(Func<TSource, TResult> selector);

    // Other members.
}

Endofunctor IEnumerable<> in DotNet category maps each T object (type) to IEnumerable<T> object (type), and its Select method maps TSource→ TResult morphism (function) to IEnumerable<TSource> → IEnumerable<TResult> morphism (function). So its Select method is of type (TSource –> TResult) –> (IEnumerable<TSource> –> IEnumerable<TResult>), which can be uncurried to (TSource –> TResult, IEnumerable<TSource>) –> IEnumerable<TResult>:

public interface IEnumerable<T> : IFunctor<IEnumerable<T>>, IEnumerable
{
    // Func<IEnumerable<TSource>, IEnumerable<TResult>> Select<TSource, TResult>(Func<TSource, TResult> selector);
    // can be equivalently converted to:
    // IEnumerable<TResult> Select<TSource, TResult>(Func<TSource, TResult> selector, IEnumerable<TSource> source);

    // Other members.
}

Now swap the 2 parameters of the uncurried Select, then its type becomes (IEnumerable<TSource>, TSource –> TResult) –> IEnumerable<TResult>:

public interface IEnumerable<T> : IFunctor<IEnumerable<T>>, IEnumerable
{
    // Func<IEnumerable<TSource>, IEnumerable<TResult>> Select<TSource, TResult>(Func<TSource, TResult> selector);
    // can be equivalently converted to:
    // IEnumerable<TResult> Select<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> selector);

    // Other members.
}

In .NET, this equivalent version of Select is exactly the LINQ query method Select. The following is the comparison of functor Select method and LINQ Select method:

public static partial class EnumerableExtensions // IEnumerable<T> : IFunctor<IEnumerable<>>
{
    // Functor Select: (TSource -> TResult) -> (IEnumerable<TSource> -> IEnumerable<TResult>).
    public static Func<IEnumerable<TSource>, IEnumerable<TResult>> Select<TSource, TResult>(
        Func<TSource, TResult> selector) => source => 
            Select(source, selector);

    // 1. Uncurry to Select: (TSource -> TResult, IEnumerable<TSource>) -> IEnumerable<TResult>.
    // 2. Swap 2 parameters to Select: (IEnumerable<TSource>, TSource -> TResult) -> IEnumerable<TResult>.
    // 3. Define as LINQ extension method.
    public static IEnumerable<TResult> Select<TSource, TResult>(
        this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
        foreach (TSource value in source)
        {
            yield return selector(value);
        }
    }
}

So the IEnumerable<> functor’s morphism mapping capability is implemented as the LINQ mapping query. As a part of the LINQ query expression pattern, functor support is built in in the C# language:

internal static void Map()
{
    IEnumerable<int> source = System.Linq.Enumerable.Range(0, 5);
    // Map int to string.
    Func<int, string> selector = Convert.ToString;
    // Map IEnumerable<int> to IEnumerable<string>.
    IEnumerable<string> query = from value in source
                                select selector(value); // Define query.
    query.WriteLines(); // Execute query.
}

And the above Select implementation satisfies the functor laws:

// using static Dixin.Linq.CategoryTheory.Functions;
internal static void FunctorLaws()
{
    IEnumerable<int> source = new int[] { 0, 1, 2, 3, 4 };
    Func<int, double> selector1 = int32 => Math.Sqrt(int32);
    Func<double, string> selector2 = @double => @double.ToString("0.00");

    // Associativity preservation: source.Select(selector2.o(selector1)) == source.Select(selector1).Select(selector2).
    (from value in source
        select selector2.o(selector1)(value)).WriteLines();  // 0.00 1.00 1.41 1.73 2.00
    (from value in source
        select selector1(value) into value
        select selector2(value)).WriteLines();  // 0.00 1.00 1.41 1.73 2.00
    // Identity preservation: source.Select(Id) == Id(source).
    (from value in source
        select Id(value)).WriteLines(); // 0 1 2 3 4
    Id(source).WriteLines(); // 0 1 2 3 4
}

Functor pattern of LINQ

So LINQ Select mapping query’s quintessential mathematics is functor. Generally, in DotNet category, a type is a functor if:

  • This type is an open generic type definition, which can be viewed as type constructor of kind * –> *, so that it maps a concrete type T to another concrete functor-wrapped type.
  • It is equipped with the standard LINQ query method Select, which can be either instance method or extension method.
  • The implementation of Select satisfies the functor laws, so that DotNet category’s associativity law and identity law are preserved.

On the other hand, to enable the LINQ functor query expression (single from clauses with select clause) for a type does not require that type to be strictly a functor. This LINQ syntax can be enabled for any generic or non generic type with as long as it has such a Select method, , which can be virtually demonstrated as:

// Cannot be compiled.
internal static void Map<TFunctor<>, TSource, TResult>( // Non generic TFunctor can work too.
    TFunctor<TSource> functor, Func<TSource, TResult> selector) where TFunctor<> : IFunctor<TFunctor<>>
{
    TFunctor<TResult> query = from /* TSource */ value in /* TFunctor<TSource> */ functor
                              select /* TResult */ selector(value); // Define query.
}

More LINQ to Functors

Many other open generic type definitions provided by .NET can be functor. Take Lazy<> as example, first, apparently it is a type constructor of kind * –> *. Then, its Select query method can be defined as extension method:

public static partial class LazyExtensions // Lazy<T> : IFunctor<Lazy<>>
{
    // Functor Select: (TSource -> TResult) -> (Lazy<TSource> -> Lazy<TResult>)
    public static Func<Lazy<TSource>, Lazy<TResult>> Select<TSource, TResult>(
        Func<TSource, TResult> selector) => source =>
            Select(source, selector);

    // LINQ Select: (Lazy<TSource>, TSource -> TResult) -> Lazy<TResult>
    public static Lazy<TResult> Select<TSource, TResult>(
        this Lazy<TSource> source, Func<TSource, TResult> selector) =>
            new Lazy<TResult>(() => selector(source.Value));

    internal static void Map()
    {
        Lazy<int> source = new Lazy<int>(() => 1);
        // Map int to string.
        Func<int, string> selector = Convert.ToString;
        // Map Lazy<int> to Lazy<string>.
        Lazy<string> query = from value in source
                             select selector(value); // Define query.
        string result = query.Value; // Execute query.
    }
}

Func<> with 1 type parameter is also a functor with the following Select implementation:

public static partial class FuncExtensions // Func<T> : IFunctor<Func<>>
{
    // Functor Select: (TSource -> TResult) -> (Func<TSource> -> Func<TResult>)
    public static Func<Func<TSource>, Func<TResult>> Select<TSource, TResult>(
        Func<TSource, TResult> selector) => source =>
            Select(source, selector);

    // LINQ Select: (Func<TSource>, TSource -> TResult) -> Func<TResult>
    public static Func<TResult> Select<TSource, TResult>(
        this Func<TSource> source, Func<TSource, TResult> selector) =>
            () => selector(source());

    internal static void Map()
    {
        Func<int> source = () => 1;
        // Map int to string.
        Func<int, string> selector = Convert.ToString;
        // Map Func<int> to Func<string>.
        Func<string> query = from value in source
                             select selector(value); // Define query.
        string result = query(); // Execute query.
    }
}

Here Select maps TSource –> TResult function to Func<TSource> –> Func<TResult> function, which is straightforward. The other Func generic delegate types, like Func<,> with 2 type parameters, could be more interesting. Just like fore mentioned ValueTuple<,>, Func<,> is of kind * –> * –> *, and can be viewed as a type constructor accepting 2 concrete types and returning another concrete type, which is different from functor. However, if Func<,> already has a concrete type T as its first type parameter, then Func<T,> can be viewed as a partially applied type constructor of kind * –> *, which can map one concrete type (its second type parameter) to another concrete type. So that Func<T,> is also a functor, with the following Select method:

public static partial class FuncExtensions // Func<T, TResult> : IFunctor<Func<T,>>
{
    // Functor Select: (TSource -> TResult) -> (Func<T, TSource> -> Func<T, TResult>)
    public static Func<Func<T, TSource>, Func<T, TResult>> Select<T, TSource, TResult>(
        Func<TSource, TResult> selector) => source =>
            Select(source, selector);

    // LINQ Select: (Func<T, TSource>, TSource -> TResult) -> Func<T, TResult>
    public static Func<T, TResult> Select<T, TSource, TResult>(
        this Func<T, TSource> source, Func<TSource, TResult> selector) =>
            value => selector(source(value)); // selector.o(source);
}

This time Select maps TSource –> TResult function to Func<T, TSource> –> Func<T, TResult> function. Actually, Func<T,> functor’s Select is exactly the function composition:

internal static void Map<T>(T input)
{
    Func<T, string> source = value => value.ToString();
    // Map string to bool.
    Func<string, bool> selector = string.IsNullOrWhiteSpace;
    // Map Func<T, string> to Func<T, bool>.
    Func<T, bool> query = from value in source
                          select selector(value); // Define query.
    bool result = query(input); // Execute query.

    // Equivalent to:
    Func<T, string> function1 = source;
    Func<string, bool> function2 = selector;
    Func<T, bool> composition = function2.o(function1);
    result = composition(input);
}

ValueTuple<> with 1 type parameter simply wraps a value. It is the eager version of Lazy<>, and it is also functor, with the following Select method:

public static partial class ValueTupleExtensions // ValueTuple<T> : IFunctor<ValueTuple<>>
{
    // Functor Select: (TSource -> TResult) -> (ValueTuple<TSource> -> ValueTuple<TResult>)
    public static Func<ValueTuple<TSource>, ValueTuple<TResult>> Select<TSource, TResult>(
        Func<TSource, TResult> selector) => source =>
            Select(source, selector); // Immediate execution.

    // LINQ Select: (ValueTuple<TSource>, TSource -> TResult) -> ValueTuple<TResult>
    public static ValueTuple<TResult> Select<TSource, TResult>(
        this ValueTuple<TSource> source, Func<TSource, TResult> selector) =>
            new ValueTuple<TResult>(selector(source.Item1)); // Immediate execution.
}

Unlike all the previous Select, here ValueTuple<>’s Select query method cannot implement deferred execution. To construct a ValueTuple<TResult> instance and return, selector must be called immediately to evaluate the result value.

internal static void Map()
{
    ValueTuple<int> source = new ValueTuple<int>(1);
    // Map int to string.
    Func<int, string> selector = int32 =>
        {
            $"{nameof(selector)} is called with {int32}.".WriteLine();
            return Convert.ToString(int32);
        };
    // Map ValueTuple<int> to ValueTuple<string>.
    ValueTuple<string> query = from value in source // Define and execute query.
                                select selector(value); // selector is called with 1.
    string result = query.Item1; // Query result.
}

Similar to Func<T,>, ValueTuple<T,> is also functor, with the following Select method of immediate execution:

public static partial class ValueTupleExtensions // ValueTuple<T, T2> : IFunctor<ValueTuple<T,>>
{
    // Functor Select: (TSource -> TResult) -> (ValueTuple<T, TSource> -> ValueTuple<T, TResult>)
    public static Func<(T, TSource), (T, TResult)> Select<T, TSource, TResult>(
        Func<TSource, TResult> selector) => source =>
            Select(source, selector); // Immediate execution.

    // LINQ Select: (ValueTuple<T, TSource>, TSource -> TResult) -> ValueTuple<T, TResult>
    public static (T, TResult) Select<T, TSource, TResult>(
        this(T, TSource) source, Func<TSource, TResult> selector) =>
            (source.Item1, selector(source.Item2)); // Immediate execution.

    internal static void Map<T>(T item1)
    {
        (T, int) source = (item1, 1);
        // Map int to string.
        Func<int, string> selector = int32 =>
        {
            $"{nameof(selector)} is called with {int32}.".WriteLine();
            return Convert.ToString(int32);
        };
        // Map ValueTuple<T, int> to ValueTuple<T, string>.
        (T, string) query = from value in source // Define and execute query.
                            select selector(value); // selector is called with 1.
        string result = query.Item2; // Query result.
    }
}

Task is also an example of functor, with the following Select method:

public static partial class TaskExtensions // Task<T> : IFunctor<Task<>>
{
    // Functor Select: (TSource -> TResult) -> (Task<TSource> -> Task<TResult>)
    public static Func<Task<TSource>, Task<TResult>> Select<TSource, TResult>(
        Func<TSource, TResult> selector) => source =>
            Select(source, selector); // Immediate execution, impure.

    // LINQ Select: (Task<TSource>, TSource -> TResult) -> Task<TResult>
    public static async Task<TResult> Select<TSource, TResult>(
        this Task<TSource> source, Func<TSource, TResult> selector) =>
            selector(await source); // Immediate execution, impure.

    internal static async Task MapAsync()
    {
        Task<int> source = System.Threading.Tasks.Task.FromResult(1);
        // Map int to string.
        Func<int, string> selector = Convert.ToString;
        // Map Task<int> to Task<string>.
        Task<string> query = from value in source
                             select selector(value); // Define and execute query.
        string result = await query; // Query result.
    }
}

Similar to ValueTuple<>, above Select implementation is not deferred either. When Select is called, if the source task is already completed, the selector function is called immediately. And unlike all the previous Select methods are pure (referential transparent and side effect free), this Select use the await syntactic sugar to construct a state machine and start it immediately. So it changes state and is impure.

Nullable<> is also an interesting type. It is of kind * –> * and the following Select method can be defined:

public static partial class NullableExtensions // Nullable<T> : IFunctor<Nullable<>>
{
    // Functor Select: (TSource -> TResult) -> (Nullable<TSource> -> Nullable<TResult>)
    public static Func<TSource?, TResult?> Select2<TSource, TResult>(
        Func<TSource, TResult> selector) where TSource : struct where TResult : struct => source =>
            Select(source, selector); // Immediate execution.

    // LINQ Select: (Nullable<TSource>, TSource -> TResult) -> Nullable<TResult>
    public static TResult? Select<TSource, TResult>(
        this TSource? source, Func<TSource, TResult> selector) where TSource : struct where TResult : struct =>
            source.HasValue ? selector(source.Value) : default; // Immediate execution.

    internal static void Map()
    {
        long? source1 = 1L;
        // Map int to string.
        Func<long, TimeSpan> selector = TimeSpan.FromTicks;
        // Map Nullable<int> to Nullable<TimeSpan>.
        TimeSpan? query1 = from value in source1
                           select selector(value); // Define and execute query.
        TimeSpan result1 = query1.Value; // Query result.

        long? source2 = null;
        // Map Nullable<int> to Nullable<TimeSpan>.
        TimeSpan? query2 = from value in source2
                           select selector(value); // Define and execute query.
        bool result2 = query2.HasValue; // Query result.
    }
}

In the above Select method, if the source Nullable<TSource> instance represents an actual value of TSource, that value is extracted to call selector, and the result is wrapped into another Nullable<TResult> instance to return; if the source represents null, selector is not called, and a Nullable<TResult> instance representing null is directly returned. There are 2 issues here. First, Nullable<>’s type parameter is constrained to be structures, so it can only map some objects of DotNet category (the value types). Second, the Select implementation cannot be deferred. As LINQ query method, deferred execution is always preferred whenever possible. So the following Optional<T> type can be defined to be used with any type parameter, and also be lazy:

public readonly struct Optional<T>
{
    private readonly Lazy<(bool, T)> factory;

    public Optional(Func<(bool, T)> factory = null) =>
        this.factory = factory == null ? null : new Lazy<(bool, T)>(factory);

    public bool HasValue => this.factory?.Value.Item1 ?? false;

    public T Value
    {
        get
        {
            if (!this.HasValue)
            {
                throw new InvalidOperationException($"{nameof(Optional<T>)} object must have a value.");
            }
            return this.factory.Value.Item2;
        }
    }
}

Optional<T> is still a structure just like Nullable<T>, so its instance cannot be null. Its parameter is not constrained, so it can wrap any valid or invalid value of any type, Its constructor accepts a factory function just like Lazy<>, s the evaluation of its wrapped value can be deferred. And the factory function returns a tuple of bool value and T value, where the bool value indicates if the other T value is a valid value, and that bool value can be returned by the the HasValue property.

internal static void Optional()
{
    int int32 = 1;
    Func<int, string> function = Convert.ToString;

    Nullable<int> nullableInt32 = new Nullable<int>(int32);
    Nullable<Func<int, string>> nullableFunction = new Nullable<Func<int, string>>(function); // Cannot be compiled.
    Nullable<string> nullableString = new Nullable<string>(); // Cannot be compiled.

    Optional<int> optionalInt32 = new Optional<int>(() => (true, int32));
    Optional<Func<int, string>> optionalFunction = new Optional<Func<int, string>>(() => true, function));
    Optional<string> optionalString = new Optional<string>(); // Equivalent to: new Optional<string>(() => false, default);
}

Apparently, Optional<> is a factor, and its Select can be defined with deferred execution:

public static partial class OptionalExtensions // Optional<T> : IFunctor<Optional<>>
{
    // Functor Select: (TSource -> TResult) -> (Optional<TSource> -> Optional<TResult>)
    public static Func<Optional<TSource>, Optional<TResult>> Select<TSource, TResult>(
        Func<TSource, TResult> selector) => source =>
            Select(source, selector);

    // LINQ Select: (Optional<TSource>, TSource -> TResult) -> Optional<TResult>
    public static Optional<TResult> Select<TSource, TResult>(
        this Optional<TSource> source, Func<TSource, TResult> selector) =>
            new Optional<TResult>(() => source.HasValue
                ? (true, selector(source.Value)) : (false, default));

    internal static void Map()
    {
        Optional<int> source1 = new Optional<int>(() => (true, 1));
        // Map int to string.
        Func<int, string> selector = Convert.ToString;
        // Map Optional<int> to Optional<string>.
        Optional<string> query1 = from value in source1
                                    select selector(value); // Define query.
        if (query1.HasValue) // Execute query.
        {
            string result1 = query1.Value;
        }

        Optional<int> source2 = new Optional<int>();
        // Map Optional<int> to Optional<string>.
        Optional<string> query2 = from value in source2
                                    select selector(value); // Define query.
        if (query2.HasValue) // Execute query.
        {
            string result2 = query2.Value;
        }
    }
}

It is easy to verify all the above Select methods satisfy the functor laws. However, not any Select can automatically satisfy the functor laws. The following is a different Select implementation for Lazy<>:

public static Lazy<TResult> Select<TSource, TResult>(
    this Lazy<TSource> source, Func<TSource, TResult> selector) =>
        new Lazy<TResult>(() => default);

And it breaks the functor because it does not preserve the identity law:

internal static void FunctorLaws()
{
    Lazy<int> lazy = new Lazy<int>(() => 1);
    Func<int, string> selector1 = Convert.ToString;
    Func<string, double> selector2 = Convert.ToDouble;

    // Associativity preservation: TFunctor<T>.Select(f2.o(f1)) == TFunctor<T>.Select(f1).Select(f2)
    lazy.Select(selector2.o(selector1)).Value.WriteLine(); // 0
    lazy.Select(selector1).Select(selector2).Value.WriteLine(); // 0
    // Identity preservation: TFunctor<T>.Select(Id) == Id(TFunctor<T>)
    lazy.Select(Id).Value.WriteLine(); // 0
    Id(lazy).Value.WriteLine(); // 1
}

37 Comments

  • Amazon.com/mytv - enter the 6 digit amazon mytv code you receive at screen at www.amazon.com/mytv to regiter your device. contact amazon support for hel

  • It was a useful article

  • This article is really fantastic and thanks for sharing the valuable post.

  • awesome post. I’m a normal visitor of your web site and appreciate you taking the time to maintain the nice site. I’ll be a frequent visitor for a long time.

  • This post is really astounding one! I was delighted to read this, very much useful. Many thanks

  • Thanks for sharing.I found a lot of interesting information here. A really good post, very thankful and hopeful that you will write many more

  • Great Article it its really informative and innovative keep us posted with new updates. its was really valuable. 

  • Thanks for writing such a good article, I stumbled onto your blog and read a few post. I like your style of writing...

  • Great Post !! Very interesting topic will bookmark your site to check if you write more about in the future.

  • Very interesting topic will bookmark your site to check if you Post more about in the future.

  • شرکت تهویه نوین ایرانیان با بهره گیری از کادری مجرب و حرفه ای، متشکل از مهندسین با تجربه و نیروهای متخصص بر آن است تا در مسیر تحقق مشتری مداری گامهایی مؤثرتر بردارد. در این راستا با ارائه محصولاتی با کیفیت، عملکردی مطلوب، هزینه ای بهینه و نیز خدمات پس از فروش، در پی جلب رضایت مشتریان گرامی است.

  • کلینیک لیزر شفا به عنوان بهترین مرکز درمان بیماری های گوارشی و نشیمن گاهی با مدیریت دکتر داود تاج بخش در رابطه با بیماریهای ناحیه مقعد خدماتی از جمله درمان بیماری هایی مثل هموروئید، فیستول، شقاق ارائه می دهد. کلینیک لیزر شفا فقط و فقط بر روی بیماری های مقعدی تمرکز داشته و تمامی متخصصین در این رابطه دور هم گرد آورده است.
    https://laser-doc.com/

  • better take assignment help than doing it alone

  • https://ma-study.blogspot.com/

  • Australian Assignment Help has qualified experts to provide assistance in assignment at affordable prices.

  • Student Life Saviour is best known for providing assignment help in South Africa at highly affordable prices.

  • Thanks for posting this blog!

  • یکی از پرطرفدار ترین خدمات شرکت حمل و نقل چینیران واردات و ترخیص کالا از چین به دبی است. حمل بار از چین به دبی به دو صورت هوایی و دریایی انجام می شود. برای کسب اطلاعات بیشتر در مورد هزینه حمل بار از چین به دبی روی لینک مقابل کلیک کنید و با کارشناسان چینیران تماس حاصل نمایید.

  • Are you looking for the best assignment help in Sydney? Assignment Prime is the best service provider in helping students with completing their descriptive essays and classwork. Get your work done with a professional approach by hiring one of our experts who are masters in different subjects. Visit our page of Assignment Writing Service Sydney.

  • بهترین برندها برای خرید لاک ژل در فروشگاه اینترنتی گلارا موجود است. اگر قصد خرید لاک ژل (به صورت تک و عمده) را دارید، محصول مورد نظر خودتان را انتخاب کنید و پس از ثبت سفارش در سراسر ایران و درب منزل به صورت اکسپرس تحویل بگیرید.

  • برخی از بازی های  شرکت بلیزارد بصورت رایگان دردسترس گیمرها و کاربران نخواهد بود. و این کاربران برای استفاده از بازی  گیم تایم یا همان گیم کارت خریداری کنند. یکی از این بازی ها،‌ بازی محبوب و پرطرفدار ورلدآف وارکرافت است. به شارژ ماهیانه بازی وارکرافت در سرورهای بازی بلیزارد  گیم تایم می گویند ، که در فروشگاه جت گیم موجود می باشد.

    خرید گیم تایم 60 روزه ازفروشگاه جت گیم:

    در واقع گیم تایم 60 روزه نمونه ای جدید است از گیم تایم ها برای استفاده دربازی World of Warcraft  . که در ادامه بیشتر در مورد این محصول و نحوه استفاده از آن توضیح می دهیم .

    شما با خرید گیم تایم 60 روزه در مدت زمان آن گیم تایم ( 60 روز ) به امکاناتی در بازی World of Warcraft درسترسی پیدا خواهید کرد که این امکانات شامل موارد زیر میباشند :

    1 - اجازه لول آپ کردن تا لول 50 ( بدون گیم تایم فقط می توانید تا لول 20 بازی کنید )

    2 - اجازه  چت کردن با دیگران درون بازی ( بدون گیم تایم نمی توانید در بازی  چت کنید )

    3 - دسترسی به بازی World of Warcraft Classic

    در نتیجه برای بازی در World of Warcraft حتمآ به تهیه گیم تایم نیاز دارید.

    نکته 1 : گیم تایم یا همان زمان بازی ورد اف وارکرفت برای توانایی انلاین بازی کردن استفاده می شود و بدون گیم تایم امکان بازی کردن بازی محبوب ورد اف وارکرفت را نخواهید داشت.

    نکته 2 : درصورتی که گیم تایم نداشته باشید امکان بازی ورد اف وارکرفت کلاسیک را ندارید و شما میتوانید جهت خرید این محصول از وبسایت ما اقدام نمایید

    نکته 3 : نیازی به وارد کردن مشخصات اکانت بلیزارد شما نمی باشد زیرا کد گیم تایم  توسط خود شما و پس از دریافت کد، وارد می شود  ( آموزش وارد کردن در پایین صفحه قرار دارد )

  • IT'S PERFECT TIME TO MAKE A FEW PLANS FOR THE FUTURE AND IT IS TIME TO BE HAPPY. I'VE LEARN THIS SUBMIT AND IF I MAY JUST I DESIRE TO RECOMMEND YOU SOME ATTENTION-GRABBING THINGS OR ADVICE. MAYBE YOU COULD WRITE SUBSEQUENT ARTICLES REFERRING TO THIS ARTICLE. I WANT TO READ EVEN MORE ISSUES APPROXIMATELY IT!

  • JUST WISH TO SAY YOUR ARTICLE IS AS AMAZING. THE CLARITY TO YOUR PUBLISH IS SIMPLY SPECTACULAR AND I COULD THINK YOU ARE A PROFESSIONAL IN THIS SUBJECT. FINE WITH YOUR PERMISSION LET ME TO GRAB YOUR FEED TO STAY UP TO DATE WITH IMPENDING POST. THANKS

  • EXCELLENT SITE YOU'VE GOT HERE.. IT IS HARD TO FIND HIGH-QUALITY WRITING LIKE YOURS NOWADAYS. I SERIOUSLY APPRECIATE INDIVIDUALS LIKE YOU! TAKE CARE!!

  • HI! IT'S AWESOME IN FAVOR OF ME TO HAVE A WEB SITE, WHICH IS HELPFUL DESIGNED FOR MY KNOW HOW. THANKS

  • pg เล่นง่ายไม่ใช้ ภาษาอังกฤษ Pgเล่นเกมง่ายไม่ต้องใช้ ภาษาอังกฤษ ก็เล่นได้เเล้วแถมยังได้ความสนุกกับเอฟเฟกสุดอลังการ เข้าเล่นได้แล้ววันนี้ ยังมีเกมที่แตกง่ายอีกเพียบเลยให้ท่านได้ทดลองเล่นอีกมากมายหลายร้อนเกมกับเว็บของเรา 

  • โปรโมชั่น สมาชิกใหม่รับโบนัส 100% <a href="https://pgslot77.bet/">pgslot77.bet</a> แจกให้กับลูกค้าใหม่ ที่สมัครเข้าใช้บริการที่เว็บสล็อตออนไลน์ค่ายดัง แจกเงินโบนัสเพิ่มเมื่อฝากแรกเข้าวันนี้

  • โปรโมชั่น ฝากแรกของวัน 20% <a href="https://pgslotpng.bet/">pgslotpng.bet</a> สำหรับทุกยูสเซอร์ ฝากเงินครั้งแรกเล่นเกมสล็อตของวันนั้น รับเพิ่มทันที โบนัส 20% ทำกำไรได้มากกว่าเดิมหลายเท่า

  • สล็อต pg <a href="https://pgdragon.cc/">pgdragon.cc</a> เว็บตรงไม่ผ่านเอเย่นต์ แหล่งรวมเกมสล็อตออนไลน์ ที่มาพร้อมเงินรางวัลก้อนใหญ๋ ทดลองเล่นฟรี บนมือถือ ได้ทุกที่ ตลอด 24 ชั่วโมง

  • Thank so much for sharing great idea with us, it help us to get more knowledge about your idea. This post are some of the best and most reasonable I've seen <a href="https://royalbritishessaywriters.co.uk/dissertation-writing-service/">Dissertation editing</a>. These kinds of deals are currently receiving a great deal of public interest. maybe due to online store owners approaching

  • Today's online gaming has a new format that has developed a game system that generates income <a href="https://pgwin888.com/">pgslot</a> It is a very popular game system to generate income for players

  • Best online games that can make a lot of money <a href="https://pgwin888.com/">pgslot</a> It's an easy-to-play online game that can be played on any platform with an internet connection

  • If you are going for best contents like I do, only visit this website all <a href=" https://vrbet69.com/">เว็บคาสิโน</a> the time for the reason that it presents quality contents, thanks

  • If you are going for best contents like I do, only visit this website all <a href=" https://vrbet69.com/">เว็บคาสิโน</a> the time for the reason that it presents quality contents, thanks

  • If you are going for best contents like I do, only visit this website all <a href=" https://vrbet69.com/">เว็บคาสิโน</a> the time for the reason that it presents quality contents, thanks

  • Blood pressure monitor watches and fitness trackers give you the freedom to take measurements as often as you choose to, even on the go. Some of these devices also count steps or monitor sleep patterns. There are also wrist blood pressure monitors that don't resemble watches.

  • خرید گیم تایم 60 روزه از جت گیم

    اگر به دنبال این هستید که یک گیم تایم 60 روزه را خریداری کنید برای بازی world of warcraft خود می توانید به فروشگاه جت گیم مراجعه کنید. یکی از ویژگی های این فروشگاه آنی بودن آن است. پس از پرداخت قیمت کد محصول به شما در سریع ترین مدت زمان تحویل داده می شود. در حال حاضر مزیت فروشگاه جت گیم همین است که نسبت به فروشگاه های دیگر سریع تر است. و با کادری مجرب و با پشتیبانی محصولات ارائه شده به کاربران با مناسب ترین قیمت در حال فعالیت می باشد.

    بهترین راه برای اکتیو کردن گیم تایم 60 روزه
    راحت ترین راه و بهترین راه برای فعال کردن گیم تایم ارائه به کلاینت بتل نت است. بعد از اینکه شما گیم تایم 60 روزه را از جت گیم خریداری کنید به شما یک کد ارسال می شود. شما باید این کد را در کلاینت بتل نت بخش Rededm a Code وارد کنید تا گیم تایم 60 روزه برای شما فعال شود.

Add a Comment

As it will appear on the website

Not displayed

Your website